[REF] website, website_*: adapt code to owl 2
Owl 2 changelog: https://github.com/odoo/owl/blob/a9f29c4caad4f32d06be1ec4780572825781cd9b/CHANGELOG.md Part-of: odoo/odoo#80156 Co-authored-by: Aaron Bohy <aab@odoo.com> Co-authored-by: Bruno Boi <boi@odoo.com> Co-authored-by: Géry Debongnie <ged@odoo.com> Co-authored-by: Samuel Degueldre <sad@odoo.com> Co-authored-by: Mathieu Duckerts-Antoine <dam@odoo.com> Co-authored-by: Simon Genin (ges) <ges@odoo.com> Co-authored-by: Francois (fge) <fge@odoo.com> Co-authored-by: Michael Mattiello (mcm) <mcm@odoo.com> Co-authored-by: Julien Mougenot <jum@odoo.com> Co-authored-by: luvi <luvi@odoo.com> Co-authored-by: Lucas Perais (lpe) <lpe@odoo.com> Co-authored-by: Jorge Pinna Puissant <jpp@odoo.com>
This commit is contained in:
co-authored by
Aaron Bohy
Bruno Boi
Géry Debongnie
Mathieu Duckerts-Antoine
Simon Genin
Francois
Michael Mattiello
Julien Mougenot
luvi
Lucas Perais
Jorge Pinna Puissant
parent
6d13271aac
commit
56cc3dfab1
@@ -8,8 +8,16 @@ import {ColorpickerWidget} from 'web.Colorpicker';
|
||||
import {_t, _lt} from 'web.core';
|
||||
import {svgToPNG} from 'website.utils';
|
||||
import {useService} from "@web/core/utils/hooks";
|
||||
import {renderToString} from "@web/core/utils/render";
|
||||
|
||||
const { Component, QWeb, RouteComponent, Router, Store, loadFile, mount, useDispatch, useGetters, useRef, useStore, whenReady } = owl;
|
||||
const { App, Component, onMounted, reactive, useEnv, useRef, useState, whenReady } = owl;
|
||||
|
||||
const ROUTES = {
|
||||
descriptionScreen: 2,
|
||||
paletteSelectionScreen: 3,
|
||||
featuresSelectionScreen: 4,
|
||||
themeSelectionScreen: 5,
|
||||
};
|
||||
|
||||
const WEBSITE_TYPES = {
|
||||
1: {id: 1, label: _lt("a business website"), name: 'business'},
|
||||
@@ -69,13 +77,13 @@ class SkipButton extends Component {
|
||||
SkipButton.template = 'website.Configurator.SkipButton';
|
||||
|
||||
class WelcomeScreen extends Component {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.dispatch = useDispatch();
|
||||
setup() {
|
||||
this.state = useStore();
|
||||
this.router = useRouter();
|
||||
}
|
||||
|
||||
goToDescription() {
|
||||
this.env.router.navigate({to: 'CONFIGURATOR_DESCRIPTION_SCREEN'});
|
||||
this.router.navigate(ROUTES.descriptionScreen);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,18 +93,18 @@ Object.assign(WelcomeScreen, {
|
||||
});
|
||||
|
||||
class DescriptionScreen extends Component {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
setup() {
|
||||
this.industrySelection = useRef('industrySelection');
|
||||
this.state = useStore((state) => state);
|
||||
this.state = useStore();
|
||||
this.router = useRouter();
|
||||
this.labelToId = {};
|
||||
this.getters = useGetters();
|
||||
this.dispatch = useDispatch();
|
||||
this.autocompleteHasResults = true;
|
||||
|
||||
onMounted(() => this.onMounted());
|
||||
}
|
||||
|
||||
mounted() {
|
||||
this.dispatch('selectWebsitePurpose', undefined);
|
||||
onMounted() {
|
||||
this.selectWebsitePurpose();
|
||||
$(this.industrySelection.el).autocomplete({
|
||||
appendTo: '.o_configurator_industry_wrapper',
|
||||
delay: 400,
|
||||
@@ -124,7 +132,7 @@ class DescriptionScreen extends Component {
|
||||
_clearIndustrySelection() {
|
||||
this.industrySelection.el.value = '';
|
||||
this.industrySelection.el.parentNode.dataset.value = '';
|
||||
this.dispatch('selectIndustry', undefined, undefined);
|
||||
this.state.selectIndustry();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -137,7 +145,7 @@ class DescriptionScreen extends Component {
|
||||
_setSelectedIndustry(label) {
|
||||
this.industrySelection.el.parentNode.dataset.value = label;
|
||||
const id = this.labelToId[label];
|
||||
this.dispatch('selectIndustry', label, id);
|
||||
this.state.selectIndustry(label, id);
|
||||
this.checkDescriptionCompletion();
|
||||
}
|
||||
|
||||
@@ -236,25 +244,23 @@ class DescriptionScreen extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
selectWebsiteType(ev) {
|
||||
const {id} = ev.target.dataset;
|
||||
this.dispatch('selectWebsiteType', id);
|
||||
selectWebsiteType(id) {
|
||||
this.state.selectWebsiteType(id);
|
||||
setTimeout(() => {
|
||||
this.industrySelection.el.focus();
|
||||
});
|
||||
this.checkDescriptionCompletion();
|
||||
}
|
||||
|
||||
selectWebsitePurpose(ev) {
|
||||
const {id} = ev.target.dataset;
|
||||
this.dispatch('selectWebsitePurpose', id);
|
||||
selectWebsitePurpose(id) {
|
||||
this.state.selectWebsitePurpose(id);
|
||||
this.checkDescriptionCompletion();
|
||||
}
|
||||
|
||||
checkDescriptionCompletion() {
|
||||
const {selectedType, selectedPurpose, selectedIndustry} = this.state;
|
||||
if (selectedType && selectedPurpose && selectedIndustry) {
|
||||
this.env.router.navigate({to: 'CONFIGURATOR_PALETTE_SELECTION_SCREEN'});
|
||||
this.router.navigate(ROUTES.paletteSelectionScreen);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -265,19 +271,17 @@ Object.assign(DescriptionScreen, {
|
||||
});
|
||||
|
||||
class PaletteSelectionScreen extends Component {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.state = useStore((state) => state);
|
||||
this.getters = useGetters();
|
||||
this.dispatch = useDispatch();
|
||||
setup() {
|
||||
this.state = useStore();
|
||||
this.router = useRouter();
|
||||
this.logoInputRef = useRef('logoSelectionInput');
|
||||
this.notification = useService("notification");
|
||||
}
|
||||
|
||||
mounted() {
|
||||
if (this.state.logo) {
|
||||
this.updatePalettes();
|
||||
}
|
||||
onMounted(() => {
|
||||
if (this.state.logo) {
|
||||
this.updatePalettes();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
uploadLogo() {
|
||||
@@ -298,7 +302,7 @@ class PaletteSelectionScreen extends Component {
|
||||
}
|
||||
});
|
||||
if (!attachment.error) {
|
||||
this.dispatch('changeLogo', data, attachment.id);
|
||||
this.state.changeLogo(data, attachment.id);
|
||||
this.updatePalettes();
|
||||
} else {
|
||||
this.notification.notify({
|
||||
@@ -321,12 +325,12 @@ class PaletteSelectionScreen extends Component {
|
||||
args: [img],
|
||||
kwargs: {mitigate: 255},
|
||||
});
|
||||
this.dispatch('setRecommendedPalette', color1, color2);
|
||||
this.state.setRecommendedPalette(color1, color2);
|
||||
}
|
||||
|
||||
selectPalette(paletteName) {
|
||||
this.dispatch('selectPalette', paletteName);
|
||||
this.env.router.navigate({to: 'CONFIGURATOR_FEATURES_SELECTION_SCREEN'});
|
||||
this.state.selectPalette(paletteName);
|
||||
this.router.navigate(ROUTES.featuresSelectionScreen);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -336,18 +340,15 @@ Object.assign(PaletteSelectionScreen, {
|
||||
});
|
||||
|
||||
class FeaturesSelectionScreen extends Component {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.state = useStore((state) => state);
|
||||
this.getters = useGetters();
|
||||
this.dispatch = useDispatch();
|
||||
setup() {
|
||||
this.state = useStore();
|
||||
this.router = useRouter();
|
||||
}
|
||||
|
||||
async buildWebsite() {
|
||||
const industryId = this.state.selectedIndustry && this.state.selectedIndustry.id;
|
||||
if (!industryId) {
|
||||
this.env.router.navigate({to: 'CONFIGURATOR_DESCRIPTION_SCREEN'});
|
||||
return;
|
||||
return this.router.navigate(ROUTES.descriptionScreen);
|
||||
}
|
||||
const params = {
|
||||
industry_id: industryId,
|
||||
@@ -362,8 +363,8 @@ class FeaturesSelectionScreen extends Component {
|
||||
if (!themes.length) {
|
||||
await applyConfigurator(this, 'theme_default');
|
||||
} else {
|
||||
this.dispatch('updateRecommendedThemes', themes);
|
||||
this.env.router.navigate({to: 'CONFIGURATOR_THEME_SELECTION_SCREEN'});
|
||||
this.state.updateRecommendedThemes(themes);
|
||||
this.router.navigate(ROUTES.themeSelectionScreen);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -374,16 +375,15 @@ Object.assign(FeaturesSelectionScreen, {
|
||||
});
|
||||
|
||||
class ThemeSelectionScreen extends Component {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.state = useStore((state) => state);
|
||||
this.getters = useGetters();
|
||||
setup() {
|
||||
this.state = useStore();
|
||||
this.router = useRouter();
|
||||
this.themeSVGPreviews = [useRef('ThemePreview1'), useRef('ThemePreview2'), useRef('ThemePreview3')];
|
||||
}
|
||||
|
||||
mounted() {
|
||||
this.state.themes.forEach((theme, idx) => {
|
||||
$(this.themeSVGPreviews[idx].el).append(theme.svg);
|
||||
onMounted(() => {
|
||||
this.state.themes.forEach((theme, idx) => {
|
||||
$(this.themeSVGPreviews[idx].el).append(theme.svg);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -394,107 +394,139 @@ class ThemeSelectionScreen extends Component {
|
||||
|
||||
ThemeSelectionScreen.template = 'website.Configurator.ThemeSelectionScreen';
|
||||
|
||||
class App extends Component {}
|
||||
class Configurator extends Component {
|
||||
setup() {
|
||||
this.router = useRouter();
|
||||
}
|
||||
}
|
||||
|
||||
Object.assign(App, {
|
||||
components: {RouteComponent},
|
||||
template: 'website.Configurator.App',
|
||||
Object.assign(Configurator, {
|
||||
components: {
|
||||
WelcomeScreen,
|
||||
DescriptionScreen,
|
||||
PaletteSelectionScreen,
|
||||
FeaturesSelectionScreen,
|
||||
ThemeSelectionScreen,
|
||||
},
|
||||
template: 'website.Configurator.Configurator',
|
||||
});
|
||||
|
||||
//---------------------------------------------------------
|
||||
// Routes
|
||||
// Router
|
||||
//---------------------------------------------------------
|
||||
|
||||
const ROUTES = [
|
||||
{name: 'CONFIGURATOR_WELCOME_SCREEN', path: '/website/configurator', component: WelcomeScreen},
|
||||
{name: 'CONFIGURATOR_WELCOME_SCREEN_FALLBACK', path: '/website/configurator/1', component: WelcomeScreen},
|
||||
{name: 'CONFIGURATOR_DESCRIPTION_SCREEN', path: '/website/configurator/2', component: DescriptionScreen},
|
||||
{name: 'CONFIGURATOR_PALETTE_SELECTION_SCREEN', path: '/website/configurator/3', component: PaletteSelectionScreen},
|
||||
{name: 'CONFIGURATOR_FEATURES_SELECTION_SCREEN', path: '/website/configurator/4', component: FeaturesSelectionScreen},
|
||||
{name: 'CONFIGURATOR_THEME_SELECTION_SCREEN', path: '/website/configurator/5', component: ThemeSelectionScreen},
|
||||
];
|
||||
class Router {
|
||||
constructor() {
|
||||
this.location = window.location.pathname;
|
||||
}
|
||||
|
||||
navigate(id) {
|
||||
this.location = `/website/configurator${id ? '/' + id : ''}`;
|
||||
history.pushState({}, '', window.location.origin + this.location);
|
||||
}
|
||||
}
|
||||
|
||||
const useRouter = () => {
|
||||
const env = useEnv();
|
||||
return useState(env.router);
|
||||
};
|
||||
|
||||
//---------------------------------------------------------
|
||||
// Store
|
||||
//---------------------------------------------------------
|
||||
|
||||
const getters = {
|
||||
class Store {
|
||||
async start() {
|
||||
Object.assign(this, await getInitialState());
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Getters
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
getWebsiteTypes() {
|
||||
return Object.values(WEBSITE_TYPES);
|
||||
},
|
||||
}
|
||||
|
||||
getSelectedType(_, id) {
|
||||
return id ? WEBSITE_TYPES[id] : undefined;
|
||||
},
|
||||
getSelectedType(id) {
|
||||
return id && WEBSITE_TYPES[id];
|
||||
}
|
||||
|
||||
getWebsitePurpose() {
|
||||
return Object.values(WEBSITE_PURPOSES);
|
||||
},
|
||||
}
|
||||
|
||||
getSelectedPurpose(_, id) {
|
||||
return id ? WEBSITE_PURPOSES[id] : undefined;
|
||||
},
|
||||
getSelectedPurpose(id) {
|
||||
return id && WEBSITE_PURPOSES[id];
|
||||
}
|
||||
|
||||
getFeatures({state}) {
|
||||
return Object.values(state.features);
|
||||
},
|
||||
getFeatures() {
|
||||
return Object.values(this.features);
|
||||
}
|
||||
|
||||
getPalettes({state}) {
|
||||
return Object.values(state.palettes);
|
||||
},
|
||||
getPalettes() {
|
||||
return Object.values(this.palettes);
|
||||
}
|
||||
|
||||
getThemeName(idx) {
|
||||
return this.themes.length > idx && this.themes[idx].name;
|
||||
}
|
||||
|
||||
getThemeName({state}, idx) {
|
||||
return state.themes.length > idx ? state.themes[idx].name : undefined;
|
||||
},
|
||||
/**
|
||||
* @param {Object} obj
|
||||
* @param {string|undefined} [obj.state]
|
||||
* @returns {string|false}
|
||||
* @returns {string | false}
|
||||
*/
|
||||
getSelectedPaletteName({state}) {
|
||||
const palette = state.selectedPalette;
|
||||
getSelectedPaletteName() {
|
||||
const palette = this.selectedPalette;
|
||||
return palette ? (palette.name || 'recommendedPalette') : false;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const actions = {
|
||||
selectWebsiteType({state}, id) {
|
||||
Object.values(state.features).filter((feature) => feature.module_state !== 'installed').forEach((feature) => {
|
||||
//-------------------------------------------------------------------------
|
||||
// Actions
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
selectWebsiteType(id) {
|
||||
Object.values(this.features).filter((feature) => feature.module_state !== 'installed').forEach((feature) => {
|
||||
feature.selected = feature.website_config_preselection.includes(WEBSITE_TYPES[id].name);
|
||||
});
|
||||
state.selectedType = id;
|
||||
},
|
||||
selectWebsitePurpose({state}, id) {
|
||||
Object.values(state.features).filter((feature) => feature.module_state !== 'installed').forEach((feature) => {
|
||||
this.selectedType = id;
|
||||
}
|
||||
|
||||
selectWebsitePurpose(id) {
|
||||
Object.values(this.features).filter((feature) => feature.module_state !== 'installed').forEach((feature) => {
|
||||
// need to check id, since we set to undefined in mount() to avoid the auto next screen on back button
|
||||
feature.selected |= id && feature.website_config_preselection.includes(WEBSITE_PURPOSES[id].name);
|
||||
});
|
||||
state.selectedPurpose = id;
|
||||
},
|
||||
selectIndustry({state}, label, id) {
|
||||
this.selectedPurpose = id;
|
||||
}
|
||||
|
||||
selectIndustry(label, id) {
|
||||
if (!label || !id) {
|
||||
state.selectedIndustry = undefined;
|
||||
this.selectedIndustry = undefined;
|
||||
} else {
|
||||
state.selectedIndustry = {id, label};
|
||||
this.selectedIndustry = { id, label };
|
||||
}
|
||||
},
|
||||
changeLogo({state}, data, attachmentId) {
|
||||
state.logo = data;
|
||||
state.logoAttachmentId = attachmentId;
|
||||
},
|
||||
selectPalette({state}, paletteName) {
|
||||
}
|
||||
|
||||
changeLogo(data, attachmentId) {
|
||||
this.logo = data;
|
||||
this.logoAttachmentId = attachmentId;
|
||||
}
|
||||
|
||||
selectPalette(paletteName) {
|
||||
if (paletteName === 'recommendedPalette') {
|
||||
state.selectedPalette = state.recommendedPalette;
|
||||
this.selectedPalette = this.recommendedPalette;
|
||||
} else {
|
||||
state.selectedPalette = state.palettes[paletteName];
|
||||
this.selectedPalette = this.palettes[paletteName];
|
||||
}
|
||||
},
|
||||
toggleFeature({state}, featureId) {
|
||||
const feature = state.features[featureId];
|
||||
}
|
||||
|
||||
toggleFeature(featureId) {
|
||||
const feature = this.features[featureId];
|
||||
const isModuleInstalled = feature.module_state === 'installed';
|
||||
feature.selected = !feature.selected || isModuleInstalled;
|
||||
},
|
||||
setRecommendedPalette({state}, color1, color2) {
|
||||
}
|
||||
|
||||
setRecommendedPalette(color1, color2) {
|
||||
if (color1 && color2) {
|
||||
if (color1 === color2) {
|
||||
color2 = ColorpickerWidget.mixCssColors('#FFFFFF', color1, 0.2);
|
||||
@@ -507,20 +539,20 @@ const actions = {
|
||||
color5: ColorpickerWidget.mixCssColors(color1, '#000000', 0.75),
|
||||
};
|
||||
CUSTOM_BG_COLOR_ATTRS.forEach((attr) => {
|
||||
recommendedPalette[attr] = recommendedPalette[state.defaultColors[attr]];
|
||||
recommendedPalette[attr] = recommendedPalette[this.defaultColors[attr]];
|
||||
});
|
||||
state.recommendedPalette = recommendedPalette;
|
||||
this.recommendedPalette = recommendedPalette;
|
||||
} else {
|
||||
state.recommendedPalette = undefined;
|
||||
this.recommendedPalette = undefined;
|
||||
}
|
||||
},
|
||||
updateRecommendedThemes({state}, themes) {
|
||||
state.themes = themes.slice(0, 3);
|
||||
}
|
||||
};
|
||||
|
||||
updateRecommendedThemes(themes) {
|
||||
this.themes = themes.slice(0, 3);
|
||||
}
|
||||
}
|
||||
|
||||
async function getInitialState() {
|
||||
|
||||
// Load values from python and iap
|
||||
var results = await rpc.query({
|
||||
model: 'website',
|
||||
@@ -596,6 +628,15 @@ async function getInitialState() {
|
||||
});
|
||||
}
|
||||
|
||||
const useStore = () => {
|
||||
const env = useEnv();
|
||||
return useState(env.state);
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
async function skipConfigurator() {
|
||||
await rpc.query({
|
||||
model: 'website',
|
||||
@@ -607,16 +648,15 @@ async function skipConfigurator() {
|
||||
|
||||
async function applyConfigurator(self, themeName) {
|
||||
if (!self.state.selectedIndustry) {
|
||||
self.env.router.navigate({to: 'CONFIGURATOR_DESCRIPTION_SCREEN'});
|
||||
return;
|
||||
return self.router.navigate(ROUTES.descriptionScreen);
|
||||
}
|
||||
if (!self.state.selectedPalette) {
|
||||
self.env.router.navigate({to: 'CONFIGURATOR_PALETTE_SELECTION_SCREEN'});
|
||||
return;
|
||||
return self.router.navigate(ROUTES.paletteSelectionScreen);
|
||||
}
|
||||
if (themeName !== undefined) {
|
||||
$('body').append(self.env.loader);
|
||||
const selectedFeatures = Object.values(self.state.features).filter((feature) => feature.selected).map((feature) => feature.id);
|
||||
const loader = renderToString('website.ThemePreview.Loader', {showTips: true});
|
||||
$('body').append(loader);
|
||||
const selectedFeatures = Object.values(self.state.features).filter((feature) => feature.selected).map((feature) => feature.id)
|
||||
let selectedPalette = self.state.selectedPalette.name;
|
||||
if (!selectedPalette) {
|
||||
selectedPalette = [
|
||||
@@ -644,48 +684,57 @@ async function applyConfigurator(self, themeName) {
|
||||
window.sessionStorage.removeItem(SESSION_STORAGE_ITEM_NAME);
|
||||
window.location = resp.url;
|
||||
}
|
||||
};
|
||||
|
||||
function updateStorage(state) {
|
||||
const newState = JSON.stringify({
|
||||
defaultColors: state.defaultColors,
|
||||
features: state.features,
|
||||
logo: state.logo,
|
||||
logoAttachmentId: state.logoAttachmentId,
|
||||
selectedIndustry: state.selectedIndustry,
|
||||
selectedPalette: state.selectedPalette,
|
||||
selectedPurpose: state.selectedPurpose,
|
||||
selectedType: state.selectedType,
|
||||
recommendedPalette: state.recommendedPalette,
|
||||
});
|
||||
window.sessionStorage.setItem(SESSION_STORAGE_ITEM_NAME, newState);
|
||||
}
|
||||
|
||||
async function makeEnvironment() {
|
||||
const env = {};
|
||||
const router = new Router(env, ROUTES);
|
||||
await router.start();
|
||||
const state = await getInitialState();
|
||||
const store = new Store({state, actions, getters});
|
||||
store.on("update", null, () => {
|
||||
const newState = {
|
||||
selectedType: store.state.selectedType,
|
||||
selectedPurpose: store.state.selectedPurpose,
|
||||
selectedIndustry: store.state.selectedIndustry,
|
||||
selectedPalette: store.state.selectedPalette,
|
||||
recommendedPalette: store.state.recommendedPalette,
|
||||
defaultColors: store.state.defaultColors,
|
||||
features: store.state.features,
|
||||
logo: store.state.logo,
|
||||
logoAttachmentId: store.state.logoAttachmentId,
|
||||
};
|
||||
window.sessionStorage.setItem(SESSION_STORAGE_ITEM_NAME, JSON.stringify(newState));
|
||||
});
|
||||
const state = reactive(new Store(), () => updateStorage(state));
|
||||
await state.start();
|
||||
updateStorage(state);
|
||||
const env = {
|
||||
state,
|
||||
router: reactive(new Router()),
|
||||
services: Component.env.services,
|
||||
};
|
||||
await session.is_bound;
|
||||
const qweb = new QWeb({translateFn: _t});
|
||||
const loaderTemplate = await loadFile('/website/static/src/xml/theme_preview.xml');
|
||||
const configuratorTemplates = await loadFile('/website/static/src/components/configurator/configurator.xml');
|
||||
qweb.addTemplates(loaderTemplate);
|
||||
qweb.addTemplates(configuratorTemplates);
|
||||
|
||||
env.loader = qweb.renderToString('website.ThemePreview.Loader', {
|
||||
showTips: true
|
||||
});
|
||||
const services = Component.env.services;
|
||||
return Object.assign(env, {router, store, qweb, services});
|
||||
return env;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Setup
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
async function setup() {
|
||||
const env = await makeEnvironment();
|
||||
if (!env.store.state.industries) {
|
||||
if (!env.state.industries) {
|
||||
await skipConfigurator();
|
||||
} else {
|
||||
mount(App, {target: document.body, env});
|
||||
const templates = await (await fetch('/website/static/src/components/configurator/configurator.xml')).text();
|
||||
const app = new App(Configurator, {
|
||||
env,
|
||||
dev: env.debug,
|
||||
templates,
|
||||
translatableAttributes: ["data-tooltip"],
|
||||
translateFn: _t,
|
||||
});
|
||||
const loaderTemplate = await (await fetch('/website/static/src/xml/theme_preview.xml')).text();
|
||||
app.addTemplates(loaderTemplate);
|
||||
renderToString.app = app;
|
||||
await app.mount(document.body);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
<t t-name="website.Configurator.SkipButton" owl="1">
|
||||
<div class="container-fluid py-2 pb-md-3 text-right pr-lg-5">
|
||||
<button class="btn btn-link" t-on-click="skip()">Skip and start from scratch</button>
|
||||
<button class="btn btn-link" t-on-click="skip">Skip and start from scratch</button>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
</div>
|
||||
<div class="lead font-weight-normal mb-4 text-600">We'll set you up and running in <b>4 steps</b>
|
||||
</div>
|
||||
<button class="o_configurator_show btn btn-primary btn-lg px-4 py-2" t-on-click="goToDescription()">Let's do it</button>
|
||||
<button class="o_configurator_show btn btn-primary btn-lg px-4 py-2" t-on-click="goToDescription">Let's do it</button>
|
||||
</div>
|
||||
</div>
|
||||
<SkipButton/>
|
||||
@@ -39,14 +39,14 @@
|
||||
<div class="w-100 px-2" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<a class="d-flex align-items-center">
|
||||
<i class="text-primary" t-if="state.selectedType">
|
||||
<t t-esc="getters.getSelectedType(state.selectedType).label" />
|
||||
<t t-esc="state.getSelectedType(state.selectedType).label"/>
|
||||
</i>
|
||||
<i class="fa fa-angle-down text-black-50 ml-auto pl-2" role="img"/>
|
||||
</a>
|
||||
</div>
|
||||
<div t-attf-class="dropdown-menu border-0 shadow-lg {{state.selectedType ? 'o_step_completed' : 'o_step_todo show'}}" role="menu">
|
||||
<t t-foreach="getters.getWebsiteTypes()" t-as="type" t-key="type.name">
|
||||
<a t-att-title="type.name" t-att-data-id="type.id" t-on-click="selectWebsiteType" class="dropdown-item o_change_website_type">
|
||||
<t t-foreach="state.getWebsiteTypes()" t-as="type" t-key="type.name">
|
||||
<a t-att-title="type.name" t-on-click="() => this.selectWebsiteType(type.id)" class="dropdown-item o_change_website_type">
|
||||
<t t-esc="type.label"/>
|
||||
</a>
|
||||
</t>
|
||||
@@ -67,14 +67,14 @@
|
||||
<div class="w-100 px-2" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<a class="d-flex align-items-center">
|
||||
<t t-if="state.selectedPurpose">
|
||||
<t t-esc="getters.getSelectedPurpose(state.selectedPurpose).label" />
|
||||
<t t-esc="state.getSelectedPurpose(state.selectedPurpose).label"/>
|
||||
</t>
|
||||
<i class="fa fa-angle-down text-black-50 ml-auto pl-2" role="img"/>
|
||||
</a>
|
||||
</div>
|
||||
<div class="dropdown-menu border-0 shadow-lg" role="menu">
|
||||
<t t-foreach="getters.getWebsitePurpose()" t-as="type" t-key="type.name">
|
||||
<a t-att-title="type.name" t-att-data-id="type.id" t-on-click="selectWebsitePurpose" class="dropdown-item o_change_website_purpose">
|
||||
<t t-foreach="state.getWebsitePurpose()" t-as="type" t-key="type.name">
|
||||
<a t-att-title="type.name" t-on-click="() => this.selectWebsitePurpose(type.id)" class="dropdown-item o_change_website_purpose">
|
||||
<t t-esc="type.label"/>
|
||||
</a>
|
||||
</t>
|
||||
@@ -110,13 +110,13 @@
|
||||
</div>
|
||||
</div>
|
||||
<div t-if="state.recommendedPalette" class="w-75 mx-auto px-2 pt-3" style="max-width: 184px;">
|
||||
<div t-attf-class="palette_card rounded-pill overflow-hidden d-flex {{getters.getSelectedPaletteName() == 'recommendedPalette' ? 'selected' : ''}}"
|
||||
t-on-click="selectPalette('recommendedPalette')" t-attf-style="background-color: {{state.recommendedPalette.color3}}">
|
||||
<div t-attf-class="palette_card rounded-pill overflow-hidden d-flex {{state.getSelectedPaletteName() == 'recommendedPalette' ? 'selected' : ''}}"
|
||||
t-on-click="() => this.selectPalette('recommendedPalette')" t-attf-style="background-color: {{state.recommendedPalette.color3}}">
|
||||
<div class="color_sample w-100" t-attf-style="background-color: {{state.recommendedPalette.color1}}"/>
|
||||
<div class="color_sample w-100" t-attf-style="background-color: {{state.recommendedPalette.color3}}"/>
|
||||
<div class="color_sample w-100" t-attf-style="background-color: {{state.recommendedPalette.color2}}"/>
|
||||
</div>
|
||||
<button class="btn btn-primary text-nowrap mt-3 d-block mx-auto" t-on-click="selectPalette('recommendedPalette')">
|
||||
<button class="btn btn-primary text-nowrap mt-3 d-block mx-auto" t-on-click="() => this.selectPalette('recommendedPalette')">
|
||||
Let's go!<i class="fa fa-angle-right text-white-50 pl-2" role="img"/>
|
||||
</button>
|
||||
</div>
|
||||
@@ -131,10 +131,10 @@
|
||||
<div class="h4 text-center">
|
||||
<b>Choose</b> a pre-made Palette</div>
|
||||
<div class="d-flex flex-wrap align-items-end">
|
||||
<t t-foreach="getters.getPalettes()" t-as="palette" t-key="palette_index">
|
||||
<t t-foreach="state.getPalettes()" t-as="palette" t-key="palette_index">
|
||||
<div class="w-50 w-md-25 px-2 pt-3">
|
||||
<div t-attf-class="palette_card rounded-pill overflow-hidden d-flex {{getters.getSelectedPaletteName() == palette.name ? 'selected' : ''}}"
|
||||
t-on-click="selectPalette(palette.name)" t-attf-style="background-color: {{palette.color3}}">
|
||||
<div t-attf-class="palette_card rounded-pill overflow-hidden d-flex {{state.getSelectedPaletteName() == palette.name ? 'selected' : ''}}"
|
||||
t-on-click="() => this.selectPalette(palette.name)" t-attf-style="background-color: {{palette.color3}}">
|
||||
<div class="color_sample w-100" t-attf-style="background-color: {{palette.color1}}"/>
|
||||
<div class="color_sample w-100" t-attf-style="background-color: {{palette.color3}}"/>
|
||||
<div class="color_sample w-100" t-attf-style="background-color: {{palette.color2}}"/>
|
||||
@@ -160,10 +160,10 @@
|
||||
<h5 class="o_configurator_show_fast text-muted pb-lg-3">You'll be able to create your pages later on.</h5>
|
||||
<div class="page_feature_selection o_configurator_show overflow-auto mt-lg-3 mx-n2">
|
||||
<div class="w-100 page_feature_selection d-flex flex-wrap">
|
||||
<t t-foreach="getters.getFeatures()" t-as="feature" t-key="feature_index">
|
||||
<t t-foreach="state.getFeatures()" t-as="feature" t-key="feature_index">
|
||||
<t t-set='isInstalled' t-value="feature.module_state == 'installed'"/>
|
||||
<div class="p-2 w-100 w-md-50 w-lg-25" t-if="feature.type != 'empty'">
|
||||
<div t-attf-class="card h-100 {{isInstalled ? 'card_installed' : (feature.selected ? 'border-success' : '')}}" t-on-click="dispatch('toggleFeature', feature.id)">
|
||||
<div t-attf-class="card h-100 {{isInstalled ? 'card_installed' : (feature.selected ? 'border-success' : '')}}" t-on-click="() => state.toggleFeature(feature.id)">
|
||||
<div class="card-body py-2">
|
||||
<t t-if="isInstalled">
|
||||
<i t-attf-class="o_configurator_feature_status fa fa-info-circle text-muted" title="Already installed"/>
|
||||
@@ -183,7 +183,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-right border-top pt-2">
|
||||
<button class="btn btn-primary btn-lg ml-3" t-on-click="buildWebsite()">Build my website</button>
|
||||
<button class="btn btn-primary btn-lg ml-3" t-on-click="buildWebsite">Build my website</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -203,29 +203,29 @@
|
||||
<div class="container">
|
||||
<div class="row pb-4 pt-5">
|
||||
<div class="col-12 col-lg-4 d-flex align-items-end mb-4 mb-lg-0">
|
||||
<t t-if="getters.getThemeName(1)">
|
||||
<t t-if="state.getThemeName(1)">
|
||||
<div class="theme_preview border rounded position-relative w-100 small o_configurator_show_fast">
|
||||
<h6 class="theme_preview_tip text-center text-muted">Click to select</h6>
|
||||
<div class="theme_svg_container rounded overflow-hidden" t-ref="ThemePreview2"/>
|
||||
<div class="button_area" t-on-click="chooseTheme(getters.getThemeName(1))"/>
|
||||
<div class="button_area" t-on-click="() => this.chooseTheme(state.getThemeName(1))"/>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
<div class="col-12 col-lg-4 d-flex align-items-end mb-4 mb-lg-0">
|
||||
<t t-if="getters.getThemeName(0)">
|
||||
<t t-if="state.getThemeName(0)">
|
||||
<div class="theme_preview border rounded position-relative w-100 o_configurator_show">
|
||||
<h6 class="theme_preview_tip text-center text-muted">Click to select</h6>
|
||||
<div class="theme_svg_container rounded overflow-hidden" t-ref="ThemePreview1"/>
|
||||
<div class="button_area" t-on-click="chooseTheme(getters.getThemeName(0))"/>
|
||||
<div class="button_area" t-on-click="() => this.chooseTheme(state.getThemeName(0))"/>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
<div class="col-12 col-lg-4 d-flex align-items-end">
|
||||
<t t-if="getters.getThemeName(2)">
|
||||
<t t-if="state.getThemeName(2)">
|
||||
<div class="theme_preview border rounded position-relative w-100 small o_configurator_show_fast">
|
||||
<h6 class="theme_preview_tip text-center text-muted">Click to select</h6>
|
||||
<div class="theme_svg_container rounded overflow-hidden" t-ref="ThemePreview3"/>
|
||||
<div class="button_area" t-on-click="chooseTheme(getters.getThemeName(2))"/>
|
||||
<div class="button_area" t-on-click="() => this.chooseTheme(state.getThemeName(2))"/>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
@@ -236,9 +236,13 @@
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="website.Configurator.App">
|
||||
<t t-name="website.Configurator.Configurator" owl="1">
|
||||
<div class="o_configurator_container">
|
||||
<RouteComponent />
|
||||
<DescriptionScreen t-if="router.location === '/website/configurator/2'"/>
|
||||
<PaletteSelectionScreen t-elif="router.location === '/website/configurator/3'"/>
|
||||
<FeaturesSelectionScreen t-elif="router.location === '/website/configurator/4'"/>
|
||||
<ThemeSelectionScreen t-elif="router.location === '/website/configurator/5'"/>
|
||||
<WelcomeScreen t-else=""/>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
|
||||
@@ -11,10 +11,10 @@ const debugMenuService = {
|
||||
const systray = document.querySelector('.o_menu_systray');
|
||||
if (systray) {
|
||||
Object.assign(env, createDebugContext(env, {categories: ["default"]}));
|
||||
mount(DebugMenuBasic, {
|
||||
target: systray,
|
||||
mount(DebugMenuBasic, systray, {
|
||||
position: 'first-child',
|
||||
env,
|
||||
templates: window.__OWL_TEMPLATES__,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,14 +15,14 @@ QUnit.module('Website Backend Dashboard', {
|
||||
assert.expect(5);
|
||||
|
||||
patch(ControlPanel.prototype, 'test.ControlPanel', {
|
||||
mounted() {
|
||||
this.__superMounted = this._super.bind(this);
|
||||
assert.step('mounted');
|
||||
this.__superMounted(...arguments);
|
||||
},
|
||||
willUnmount() {
|
||||
assert.step('willUnmount');
|
||||
this.__superMounted(...arguments);
|
||||
setup() {
|
||||
this._super();
|
||||
owl.onMounted(() => {
|
||||
assert.step('mounted');
|
||||
});
|
||||
owl.onWillUnmount(() => {
|
||||
assert.step('willUnmount');
|
||||
})
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ var core = require('web.core');
|
||||
var Dialog = require('web.Dialog');
|
||||
var wysiwygLoader = require('web_editor.loader');
|
||||
var publicWidget = require('web.public.widget');
|
||||
const { Markup } = require('web.utils');
|
||||
var session = require('web.session');
|
||||
var qweb = core.qweb;
|
||||
|
||||
@@ -253,10 +254,9 @@ publicWidget.registry.websiteForum = publicWidget.Widget.extend({
|
||||
// translation, to fix in the appropriate version
|
||||
notifOptions.message = `${karma} ${_t("karma is required to perform this action. ")}`;
|
||||
if (forumID) {
|
||||
notifOptions.messageIsHtml = true;
|
||||
const linkLabel = _.escape(_t("Read the guidelines to know how to gain karma."));
|
||||
notifOptions.message = `
|
||||
${_.escape(notifOptions.message)}<br/>
|
||||
const linkLabel = _t("Read the guidelines to know how to gain karma.");
|
||||
notifOptions.message = Markup`
|
||||
${notifOptions.message}<br/>
|
||||
<a class="alert-link" href="/forum/${forumID}/faq">${linkLabel}</a>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -10,8 +10,8 @@ QUnit.module('activity_tests.js', {
|
||||
beforeEach(this);
|
||||
this.start = async params => {
|
||||
const res = await start({ ...params, data: this.data });
|
||||
const { components, env, widget } = res;
|
||||
this.components = components;
|
||||
const { apps, env, widget } = res;
|
||||
this.apps = apps;
|
||||
this.env = env;
|
||||
this.widget = widget;
|
||||
return res;
|
||||
|
||||
Reference in New Issue
Block a user