summaryrefslogtreecommitdiff
path: root/addons/project/static/src
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/project/static/src
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/project/static/src')
-rw-r--r--addons/project/static/src/css/project.css76
-rw-r--r--addons/project/static/src/img/app_store.pngbin0 -> 4682 bytes
-rw-r--r--addons/project/static/src/img/bird.jpgbin0 -> 10802 bytes
-rw-r--r--addons/project/static/src/img/chrome_store.pngbin0 -> 5919 bytes
-rw-r--r--addons/project/static/src/img/planner_icon.pngbin0 -> 2029 bytes
-rw-r--r--addons/project/static/src/img/play_store.pngbin0 -> 8171 bytes
-rw-r--r--addons/project/static/src/img/project-custom-tasks.gifbin0 -> 18530 bytes
-rw-r--r--addons/project/static/src/img/tasks_icon.pngbin0 -> 2545 bytes
-rw-r--r--addons/project/static/src/img/top_left_arrow.pngbin0 -> 4187 bytes
-rw-r--r--addons/project/static/src/img/web_planner_email.pngbin0 -> 4073 bytes
-rw-r--r--addons/project/static/src/img/web_planner_project.pngbin0 -> 42631 bytes
-rw-r--r--addons/project/static/src/img/web_planner_subtype.pngbin0 -> 7477 bytes
-rw-r--r--addons/project/static/src/js/portal_rating.js32
-rw-r--r--addons/project/static/src/js/project_calendar.js22
-rw-r--r--addons/project/static/src/js/project_kanban.js71
-rw-r--r--addons/project/static/src/js/project_rating_reporting.js69
-rw-r--r--addons/project/static/src/js/project_task_kanban_examples.js120
-rw-r--r--addons/project/static/src/js/tours/project.js112
-rw-r--r--addons/project/static/src/scss/portal_rating.scss34
-rw-r--r--addons/project/static/src/scss/project_dashboard.scss51
-rw-r--r--addons/project/static/src/xml/project_templates.xml17
21 files changed, 604 insertions, 0 deletions
diff --git a/addons/project/static/src/css/project.css b/addons/project/static/src/css/project.css
new file mode 100644
index 00000000..a88d7d1b
--- /dev/null
+++ b/addons/project/static/src/css/project.css
@@ -0,0 +1,76 @@
+
+.oe_kanban_project_avatars img {
+ width: 30px;
+ border-radius: 2px;
+ -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
+ -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
+ -box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
+}
+.oe_form_gantt_avatars:after {
+ font-family: "mnmliconsRegular" !important;
+ font-size: 21px;
+ font-weight: 300 !important;
+ content: "y";
+ top: 3px;
+ position: relative;
+}
+
+.openerp .oe_kanban_view .oe_kanban_project {
+ width: 250px;
+ min-height: 160px !important;
+ cursor: default;
+}
+
+.openerp .oe_percent strong:after {
+ content: "%";
+}
+
+.openerp .oe_margin_top_8 {
+ margin-top: 8px;
+}
+.openerp .oe_kanban_project .oe_kanban_project_list .col-lg-6 a{
+ margin-left: 5px;
+}
+
+/* Kanban status as label in project stage form view */
+.openerp label.oe_project_kanban_legend {
+ min-width: inherit !important;
+ margin-top: 6px;
+ margin-right: 8px;
+}
+
+.o_form_project_tasks.o_form_editable .oe_title {
+ max-width: initial;
+}
+
+.o_form_project_tasks.o_form_editable .oe_title .o_task_name {
+ /*
+ * should be (coming from addons/web/static/src/scss/form_view.scss):
+ * max-width: map-get($container-max-widths, md) - (2 * $o-horizontal-padding);
+ */
+ max-width: 688px;
+ margin-right: auto;
+}
+
+.o_kanban_project_tasks .badge {
+ background: inherit;
+ color: inherit;
+ border: 1px solid var(--success);
+}
+
+.o_kanban_project_tasks .badge-warning {
+ border-color: var(--warning);
+}
+
+.o_kanban_project_tasks .badge-danger {
+ border-color: var(--danger);
+}
+
+.o_form_project_tasks .ribbon:not(.o_invisible_modifier) + div > h1 > div[name="kanban_state"] {
+ margin-right: 150px;
+ padding-left: 1rem;
+}
+
+.o_form_project_recurrence_message *:last-child {
+ margin-bottom: 0;
+}
diff --git a/addons/project/static/src/img/app_store.png b/addons/project/static/src/img/app_store.png
new file mode 100644
index 00000000..4d79d760
--- /dev/null
+++ b/addons/project/static/src/img/app_store.png
Binary files differ
diff --git a/addons/project/static/src/img/bird.jpg b/addons/project/static/src/img/bird.jpg
new file mode 100644
index 00000000..39e96100
--- /dev/null
+++ b/addons/project/static/src/img/bird.jpg
Binary files differ
diff --git a/addons/project/static/src/img/chrome_store.png b/addons/project/static/src/img/chrome_store.png
new file mode 100644
index 00000000..95632ed5
--- /dev/null
+++ b/addons/project/static/src/img/chrome_store.png
Binary files differ
diff --git a/addons/project/static/src/img/planner_icon.png b/addons/project/static/src/img/planner_icon.png
new file mode 100644
index 00000000..6d6d7c94
--- /dev/null
+++ b/addons/project/static/src/img/planner_icon.png
Binary files differ
diff --git a/addons/project/static/src/img/play_store.png b/addons/project/static/src/img/play_store.png
new file mode 100644
index 00000000..73dd393c
--- /dev/null
+++ b/addons/project/static/src/img/play_store.png
Binary files differ
diff --git a/addons/project/static/src/img/project-custom-tasks.gif b/addons/project/static/src/img/project-custom-tasks.gif
new file mode 100644
index 00000000..e63af1ff
--- /dev/null
+++ b/addons/project/static/src/img/project-custom-tasks.gif
Binary files differ
diff --git a/addons/project/static/src/img/tasks_icon.png b/addons/project/static/src/img/tasks_icon.png
new file mode 100644
index 00000000..5c5073b1
--- /dev/null
+++ b/addons/project/static/src/img/tasks_icon.png
Binary files differ
diff --git a/addons/project/static/src/img/top_left_arrow.png b/addons/project/static/src/img/top_left_arrow.png
new file mode 100644
index 00000000..923b414b
--- /dev/null
+++ b/addons/project/static/src/img/top_left_arrow.png
Binary files differ
diff --git a/addons/project/static/src/img/web_planner_email.png b/addons/project/static/src/img/web_planner_email.png
new file mode 100644
index 00000000..971a26fe
--- /dev/null
+++ b/addons/project/static/src/img/web_planner_email.png
Binary files differ
diff --git a/addons/project/static/src/img/web_planner_project.png b/addons/project/static/src/img/web_planner_project.png
new file mode 100644
index 00000000..14db9ea0
--- /dev/null
+++ b/addons/project/static/src/img/web_planner_project.png
Binary files differ
diff --git a/addons/project/static/src/img/web_planner_subtype.png b/addons/project/static/src/img/web_planner_subtype.png
new file mode 100644
index 00000000..1dc027c1
--- /dev/null
+++ b/addons/project/static/src/img/web_planner_subtype.png
Binary files differ
diff --git a/addons/project/static/src/js/portal_rating.js b/addons/project/static/src/js/portal_rating.js
new file mode 100644
index 00000000..a333b255
--- /dev/null
+++ b/addons/project/static/src/js/portal_rating.js
@@ -0,0 +1,32 @@
+odoo.define('website_rating_project.rating', function (require) {
+'use strict';
+
+var time = require('web.time');
+var publicWidget = require('web.public.widget');
+
+publicWidget.registry.ProjectRatingImage = publicWidget.Widget.extend({
+ selector: '.o_portal_project_rating .o_rating_image',
+
+ /**
+ * @override
+ */
+ start: function () {
+ this.$el.popover({
+ placement: 'bottom',
+ trigger: 'hover',
+ html: true,
+ content: function () {
+ var $elem = $(this);
+ var id = $elem.data('id');
+ var ratingDate = $elem.data('rating-date');
+ var baseDate = time.auto_str_to_date(ratingDate);
+ var duration = moment(baseDate).fromNow();
+ var $rating = $('#rating_' + id);
+ $rating.find('.rating_timeduration').text(duration);
+ return $rating.html();
+ },
+ });
+ return this._super.apply(this, arguments);
+ },
+});
+});
diff --git a/addons/project/static/src/js/project_calendar.js b/addons/project/static/src/js/project_calendar.js
new file mode 100644
index 00000000..51b3488e
--- /dev/null
+++ b/addons/project/static/src/js/project_calendar.js
@@ -0,0 +1,22 @@
+odoo.define('project.ProjectCalendarView', function (require) {
+"use strict";
+
+const CalendarController = require('web.CalendarController');
+const CalendarView = require('web.CalendarView');
+const viewRegistry = require('web.view_registry');
+
+const ProjectCalendarController = CalendarController.extend({
+ _renderButtonsParameters() {
+ return _.extend({}, this._super(...arguments), {scaleDrop: true});
+ },
+});
+
+const ProjectCalendarView = CalendarView.extend({
+ config: _.extend({}, CalendarView.prototype.config, {
+ Controller: ProjectCalendarController,
+ }),
+ });
+
+viewRegistry.add('project_calendar', ProjectCalendarView);
+return ProjectCalendarView;
+});
diff --git a/addons/project/static/src/js/project_kanban.js b/addons/project/static/src/js/project_kanban.js
new file mode 100644
index 00000000..41aa7e7b
--- /dev/null
+++ b/addons/project/static/src/js/project_kanban.js
@@ -0,0 +1,71 @@
+odoo.define('project.project_kanban', function (require) {
+'use strict';
+
+var KanbanController = require('web.KanbanController');
+var KanbanView = require('web.KanbanView');
+var KanbanColumn = require('web.KanbanColumn');
+var view_registry = require('web.view_registry');
+var KanbanRecord = require('web.KanbanRecord');
+
+KanbanRecord.include({
+ //--------------------------------------------------------------------------
+ // Private
+ //--------------------------------------------------------------------------
+
+ /**
+ * @override
+ * @private
+ */
+ // YTI TODO: Should be transformed into a extend and specific to project
+ _openRecord: function () {
+ if (this.selectionMode !== true && this.modelName === 'project.project' &&
+ this.$(".o_project_kanban_boxes a").length) {
+ this.$('.o_project_kanban_boxes a').first().click();
+ } else {
+ this._super.apply(this, arguments);
+ }
+ },
+});
+
+var ProjectKanbanController = KanbanController.extend({
+ custom_events: _.extend({}, KanbanController.prototype.custom_events, {
+ 'kanban_column_delete_wizard': '_onDeleteColumnWizard',
+ }),
+
+ _onDeleteColumnWizard: function (ev) {
+ ev.stopPropagation();
+ const self = this;
+ const column_id = ev.target.id;
+ var state = this.model.get(this.handle, {raw: true});
+ this._rpc({
+ model: 'project.task.type',
+ method: 'unlink_wizard',
+ args: [column_id],
+ context: state.getContext(),
+ }).then(function (res) {
+ self.do_action(res);
+ });
+ }
+});
+
+var ProjectKanbanView = KanbanView.extend({
+ config: _.extend({}, KanbanView.prototype.config, {
+ Controller: ProjectKanbanController
+ }),
+});
+
+KanbanColumn.include({
+ _onDeleteColumn: function (event) {
+ event.preventDefault();
+ if (this.modelName === 'project.task') {
+ this.trigger_up('kanban_column_delete_wizard');
+ return;
+ }
+ this._super.apply(this, arguments);
+ }
+});
+
+view_registry.add('project_kanban', ProjectKanbanView);
+
+return ProjectKanbanController;
+});
diff --git a/addons/project/static/src/js/project_rating_reporting.js b/addons/project/static/src/js/project_rating_reporting.js
new file mode 100644
index 00000000..8e393c67
--- /dev/null
+++ b/addons/project/static/src/js/project_rating_reporting.js
@@ -0,0 +1,69 @@
+odoo.define('project.project_rating_reporting', function (require) {
+'use strict';
+
+const core = require('web.core');
+const _t = core._t;
+
+const viewRegistry = require('web.view_registry');
+
+const PivotController = require('web.PivotController');
+const PivotView = require('web.PivotView');
+
+const GraphController = require('web.GraphController');
+const GraphView = require('web.GraphView');
+
+var ProjectPivotController = PivotController.extend({
+ /**
+ * @override
+ */
+ init: function () {
+ this._super.apply(this, arguments);
+ var measures = JSON.parse(JSON.stringify(this.measures));
+ if ('res_id' in measures) {
+ measures.res_id.string = _t('Task');
+ }
+ if ('parent_res_id' in measures) {
+ measures.parent_res_id.string = _t('Project');
+ }
+ if ('rating' in measures) {
+ measures.rating.string = _t('Rating Value (/5)');
+ }
+ this.measures = measures;
+ },
+});
+
+var ProjectPivotView = PivotView.extend({
+ config: _.extend({}, PivotView.prototype.config, {
+ Controller: ProjectPivotController,
+ }),
+});
+
+viewRegistry.add('project_rating_pivot', ProjectPivotView);
+
+var ProjectGraphController = GraphController.extend({
+ /**
+ * @override
+ */
+ init: function () {
+ this._super.apply(this, arguments);
+ _.each(this.measures, measure => {
+ if (measure.fieldName === 'res_id') {
+ measure.description = _t('Task');
+ } else if (measure.fieldName === 'parent_res_id') {
+ measure.description = _t('Project');
+ } else if (measure.fieldName === 'rating') {
+ measure.description = _t('Rating Value (/5)');
+ }
+ });
+ },
+});
+
+var ProjectGraphView = GraphView.extend({
+ config: _.extend({}, GraphView.prototype.config, {
+ Controller: ProjectGraphController,
+ }),
+});
+
+viewRegistry.add('project_rating_graph', ProjectGraphView);
+
+});
diff --git a/addons/project/static/src/js/project_task_kanban_examples.js b/addons/project/static/src/js/project_task_kanban_examples.js
new file mode 100644
index 00000000..9cce29fc
--- /dev/null
+++ b/addons/project/static/src/js/project_task_kanban_examples.js
@@ -0,0 +1,120 @@
+odoo.define('project.task_kanban_examples', function (require) {
+'use strict';
+
+var core = require('web.core');
+var kanbanExamplesRegistry = require('web.kanban_examples_registry');
+
+var _lt = core._lt;
+
+var greenBullet = '<span class="o_status o_status_green"></span>';
+var redBullet = '<span class="o_status o_status_red"></span>';
+var star = '<a style="color: gold;" class="fa fa-star"/>';
+var clock = '<a class="fa fa-clock-o" />'
+
+var description_activities = escFormat(_lt('%s Use the %s icon to organize your daily activities.'), '<br/>', clock);
+var description = escFormat(_lt('Prioritize Tasks by using the %s icon.'+
+ '%s Use the %s button to signalize to your colleagues that a task is ready for the next stage.'+
+ '%s Use the %s to signalize a problem or a need for discussion on a task.'+
+ '%s'), star, '<br/>', greenBullet, '<br/>', redBullet, description_activities);
+
+/**
+ * Helper function to escape a text before formatting it.
+ *
+ * First argument is the string to format and the other arguments are the values
+ * to inject into the string.
+ *
+ * Sort of 'lazy escaping' as it is used alongside _lt.
+ *
+ * @returns {string} the formatted and escaped string
+ */
+function escFormat() {
+ var args = arguments;
+ return {
+ toString: function () {
+ args[0] = _.escape(args[0]);
+ return _.str.sprintf.apply(_.str, args);
+ },
+ };
+}
+
+kanbanExamplesRegistry.add('project', {
+ ghostColumns: [_lt('New'), _lt('Assigned'), _lt('In Progress'), _lt('Done')],
+ applyExamplesText: _lt("Use This For My Project"),
+ examples:[{
+ name: _lt('Software Development'),
+ columns: [_lt('Backlog'), _lt('Specifications'), _lt('Development'), _lt('Tests'), _lt('Delivered')],
+ description: escFormat(_lt('Prioritize Tasks by using the %s icon.'+
+ '%s Use the %s button to inform your colleagues that a task is ready for the next stage.'+
+ '%s Use the %s to indicate a problem or a need for discussion on a task.'+
+ '%s'), star, '<br/>', greenBullet, '<br/>', redBullet, description_activities),
+ bullets: [greenBullet, redBullet, star],
+ }, {
+ name: _lt('Agile Scrum'),
+ columns: [_lt('Backlog'), _lt('Sprint Backlog'), _lt('Sprint in Progress'), _lt('Sprint Complete'), _lt('Old Completed Sprint')],
+ description: escFormat(_lt('Waiting for the next stage: use %s and %s bullets. %s'), greenBullet, redBullet, description_activities),
+ bullets: [greenBullet, redBullet],
+ }, {
+ name: _lt('Digital Marketing'),
+ columns: [_lt('Ideas'), _lt('Researching'), _lt('Writing'), _lt('Editing'), _lt('Done')],
+ description: escFormat(_lt('Everyone can propose ideas, and the Editor marks the best ones ' +
+ 'as %s. Attach all documents or links to the task directly, to have all information about ' +
+ 'a research centralized. %s'), greenBullet, description_activities),
+ bullets: [greenBullet, redBullet],
+ }, {
+ name: _lt('Customer Feedback'),
+ columns: [_lt('New'), _lt('In development'), _lt('Done'), _lt('Refused')],
+ description: escFormat(_lt('Customers propose feedbacks by email; Odoo creates tasks ' +
+ 'automatically, and you can communicate on the task directly. Your managers decide which ' +
+ 'feedback is accepted %s and which feedback is moved to the %s column. %s'), greenBullet, _lt('"Refused"'), description_activities),
+ bullets: [greenBullet, redBullet],
+ }, {
+ name: _lt('Getting Things Done (GTD)'),
+ columns: [_lt('Inbox'), _lt('Today'), _lt('This Week'), _lt('This Month'), _lt('Long Term')],
+ description: escFormat(_lt('Fill your Inbox easily with the email gateway. Periodically review your ' +
+ 'Inbox and schedule tasks by moving them to others columns. Every day, you review the ' +
+ '%s column to move important tasks %s. Every Monday, you review the %s column. %s'), _lt('"This Week"'), _lt('"Today"'), _lt('"This Month"'), description_activities),
+ }, {
+ name: _lt('Consulting'),
+ columns: [_lt('New Projects'), _lt('Resources Allocation'), _lt('In Progress'), _lt('Done')],
+ description: escFormat(_lt('Manage the lifecycle of your project using the kanban view. Add newly acquired projects, assign them and use the %s and %s to define if the project is ready for the next step. %s'), greenBullet, redBullet, description_activities),
+ bullets: [greenBullet, redBullet],
+ }, {
+ name: _lt('Research Project'),
+ columns: [_lt('Brainstorm'), _lt('Research'), _lt('Draft'), _lt('Final Document')],
+ description: escFormat(_lt('Handle your idea gathering within Tasks of your new Project and discuss them in the chatter of the tasks. Use the %s and %s to signalize what is the current status of your Idea. %s'), greenBullet, redBullet, description_activities),
+ bullets: [greenBullet, redBullet],
+ }, {
+ name: _lt('Website Redesign'),
+ columns: [_lt('Page Ideas'), _lt('Copywriting'), _lt('Design'), _lt('Live')],
+ description: escFormat(_lt('Handle your idea gathering within Tasks of your new Project and discuss them in the chatter of the tasks. Use the %s and %s to signalize what is the current status of your Idea. %s'), greenBullet, redBullet, description_activities),
+ }, {
+ name: _lt('T-shirt Printing'),
+ columns: [_lt('New Orders'), _lt('Logo Design'), _lt('To Print'), _lt('Done')],
+ description: escFormat(_lt('Communicate with customers on the task using the email gateway. ' +
+ 'Attach logo designs to the task, so that information flow from designers to the workers ' +
+ 'who print the t-shirt. Organize priorities amongst orders %s using the icon. %s'), star, description_activities),
+ bullets: [star],
+ }, {
+ name: _lt('Design'),
+ columns: [_lt('New Request'), _lt('Design'), _lt('Client Review'), _lt('Handoff')],
+ description: description,
+ bullets: [greenBullet, redBullet, star, clock],
+ }, {
+ name: _lt('Publishing'),
+ columns: [_lt('Ideas'), _lt('Writing'), _lt('Editing'), _lt('Published')],
+ description: description,
+ bullets: [greenBullet, redBullet, star, clock],
+ }, {
+ name: _lt('Manufacturing'),
+ columns: [_lt('New Orders'), _lt('Material Sourcing'), _lt('Manufacturing'), _lt('Assembling'), _lt('Delivered')],
+ description: description,
+ bullets: [greenBullet, redBullet, star, clock],
+ }, {
+ name: _lt('Podcast and Video Production'),
+ columns: [_lt('Research'), _lt('Script'), _lt('Recording'), _lt('Mixing'), _lt('Publishing')],
+ description: description,
+ bullets: [greenBullet, redBullet, star, clock],
+ }],
+});
+
+});
diff --git a/addons/project/static/src/js/tours/project.js b/addons/project/static/src/js/tours/project.js
new file mode 100644
index 00000000..df8ef5d7
--- /dev/null
+++ b/addons/project/static/src/js/tours/project.js
@@ -0,0 +1,112 @@
+odoo.define('project.tour', function(require) {
+"use strict";
+
+var core = require('web.core');
+var tour = require('web_tour.tour');
+
+var _t = core._t;
+
+tour.register('project_tour', {
+ sequence: 110,
+ url: "/web",
+ rainbowManMessage: "Congratulations, you are now a master of project management.",
+}, [tour.stepUtils.showAppsMenuItem(), {
+ trigger: '.o_app[data-menu-xmlid="project.menu_main_pm"]',
+ content: _t('Want a better way to <b>manage your projects</b>? <i>It starts here.</i>'),
+ position: 'right',
+ edition: 'community',
+}, {
+ trigger: '.o_app[data-menu-xmlid="project.menu_main_pm"]',
+ content: _t('Want a better way to <b>manage your projects</b>? <i>It starts here.</i>'),
+ position: 'bottom',
+ edition: 'enterprise',
+}, {
+ trigger: '.o-kanban-button-new',
+ extra_trigger: '.o_project_kanban',
+ content: _t('Let\'s create your first <b>project</b>.'),
+ position: 'bottom',
+ width: 200,
+}, {
+ trigger: 'input.o_project_name',
+ content: _t('Choose a <b>name</b> for your project. <i>It can be anything you want: the name of a customer,\
+ of a product, of a team, of a construction site...</i>'),
+ position: 'right',
+}, {
+ trigger: '.o_open_tasks',
+ content: _t('Let\'s create your first <b>project</b>.'),
+ position: 'top',
+ run: function (actions) {
+ actions.auto('.modal:visible .btn.btn-primary');
+ },
+}, {
+ trigger: ".o_kanban_project_tasks .o_column_quick_create input",
+ content: _t("Add columns to organize your tasks into <b>stages</b> <i>e.g. New - In Progress - Done</i>."),
+ position: 'bottom',
+}, {
+ trigger: ".o_kanban_project_tasks .o_column_quick_create .o_kanban_add",
+ auto: true,
+}, {
+ trigger: ".o_kanban_project_tasks .o_column_quick_create input",
+ extra_trigger: '.o_kanban_group',
+ content: _t("Add columns to organize your tasks into <b>stages</b> <i>e.g. New - In Progress - Done</i>."),
+ position: 'bottom',
+}, {
+ trigger: ".o_kanban_project_tasks .o_column_quick_create .o_kanban_add",
+ auto: true,
+}, {
+ trigger: '.o-kanban-button-new',
+ extra_trigger: '.o_kanban_group:eq(1)',
+ content: _t("Let's create your first <b>task</b>."),
+ position: 'bottom',
+ width: 200,
+}, {
+ trigger: '.o_kanban_quick_create input.o_field_char[name=name]',
+ extra_trigger: '.o_kanban_project_tasks',
+ content: _t('Choose a task <b>name</b> <i>(e.g. Website Design, Purchase Goods...)</i>'),
+ position: 'right',
+}, {
+ trigger: '.o_kanban_quick_create .o_kanban_add',
+ extra_trigger: '.o_kanban_project_tasks',
+ content: _t("Add your task once it is ready."),
+ position: "bottom",
+}, {
+ trigger: ".o_kanban_record .oe_kanban_content",
+ extra_trigger: '.o_kanban_project_tasks',
+ content: _t("<b>Drag &amp; drop</b> the card to change your task from stage."),
+ position: "bottom",
+ run: "drag_and_drop .o_kanban_group:eq(1) ",
+}, {
+ trigger: ".o_kanban_record:first",
+ extra_trigger: '.o_kanban_project_tasks',
+ content: _t("Let's start working on your task."),
+ position: "bottom",
+}, {
+ trigger: ".o_ChatterTopbar_buttonSendMessage",
+ content: _t("Use this chatter to <b>send emails</b> and communicate efficently with your customers. \
+ Add new people in the followers list to make them aware about the main changes about this task."),
+ width: 350,
+ position: "bottom",
+}, {
+ trigger: ".o_ChatterTopbar_buttonLogNote",
+ content: _t("<b>Log notes</b> for internal communications <i>(the people following this task won't be notified \
+ of the note you are logging unless you specifically tag them)</i>. Use @ <b>mentions</b> to ping a colleague \
+ or # <b>mentions</b> to reach an entire team."),
+ width: 350,
+ position: "bottom"
+}, {
+ trigger: ".o_ChatterTopbar_buttonScheduleActivity",
+ content: _t("Use <b>activities</b> to organize your daily work."),
+}, {
+ trigger: ".modal-dialog .btn-primary",
+ content: "Schedule your activity once it is ready",
+ position: "bottom",
+ run: "click",
+}, {
+ trigger: ".breadcrumb-item:not(.active):last",
+ extra_trigger: '.o_form_project_tasks.o_form_readonly',
+ content: _t("Let's go back to your <b>kanban view</b> to have an overview of your next tasks."),
+ position: "right",
+ run: 'click',
+}]);
+
+});
diff --git a/addons/project/static/src/scss/portal_rating.scss b/addons/project/static/src/scss/portal_rating.scss
new file mode 100644
index 00000000..4ae3290a
--- /dev/null
+++ b/addons/project/static/src/scss/portal_rating.scss
@@ -0,0 +1,34 @@
+.o_portal_project_rating {
+ .thumbnail{
+ height: 240px;
+ }
+ .o_top_partner_rating_image {
+ height: 15px;
+ }
+ .o_top_partner_image {
+ height: 30px;
+ width: 30px;
+ }
+ .o_top_partner_feedback{
+ word-wrap: break-word;
+ }
+ .o_vertical_separator {
+ border-left: 1px solid #eeeeee
+ }
+ .o_rating_progress {
+ margin-bottom: 10px;
+ }
+ .o_rating_count {
+ display: inline-block;
+ min-width: 22px
+ }
+ .o_smiley_no_padding_left {
+ padding-left: 0;
+ }
+ .o_smiley_no_padding_right {
+ padding-right: 0;
+ }
+ .o_lighter_smileys {
+ opacity: 0.4
+ }
+} \ No newline at end of file
diff --git a/addons/project/static/src/scss/project_dashboard.scss b/addons/project/static/src/scss/project_dashboard.scss
new file mode 100644
index 00000000..73539824
--- /dev/null
+++ b/addons/project/static/src/scss/project_dashboard.scss
@@ -0,0 +1,51 @@
+.o_kanban_view.o_kanban_dashboard.o_project_kanban {
+ .o_project_kanban_boxes {
+ display: flex;
+ flex-flow: row wrap;
+ justify-content: space-between;
+
+ .o_project_kanban_box {
+ position: relative;
+ text-align: center;
+ padding: 0 0 0 0;
+ margin: 0 5px;
+
+ .o_value {
+ font-weight: 800;
+ }
+
+ > div {
+ font-weight: 500;
+
+ button.o_needaction {
+ font-size: small;
+ font-weight: 400;
+ margin-left: 4px;
+ @include o-hover-opacity(0.5, 1);
+
+ &:before {
+ content: "/ ";
+ }
+
+ &:after {
+ content: "\f086";
+ font: normal normal normal 14px/1 FontAwesome;
+ }
+ }
+ }
+ }
+ }
+}
+
+.o_dow_widget {
+ th {
+ padding: 10px 10px 0 10px;
+ }
+
+ .o_dow_days {
+ td {
+ text-align: center;
+ vertical-align: middle;
+ }
+ }
+}
diff --git a/addons/project/static/src/xml/project_templates.xml b/addons/project/static/src/xml/project_templates.xml
new file mode 100644
index 00000000..f30d8c95
--- /dev/null
+++ b/addons/project/static/src/xml/project_templates.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<templates id="template" xml:space="preserve">
+ <t t-name="day_of_week_widget">
+ <table class="o_dow_widget">
+ <thead>
+ <tr>
+ <th t-foreach="widget.labels" t-as="label">
+ <t t-esc="label"/>
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr class="o_dow_days" />
+ </tbody>
+ </table>
+ </t>
+</templates>