From ebb86591e8ce22d2036294683164ac2b36c7ae79 Mon Sep 17 00:00:00 2001 From: Henry Jonas Date: Thu, 5 Dec 2024 14:15:11 -0400 Subject: [PATCH 01/30] FOUR-20583: S2: Improve vendors --- webpack.mix.js | 49 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 40 insertions(+), 9 deletions(-) diff --git a/webpack.mix.js b/webpack.mix.js index ef6c05502a..e4915f4519 100644 --- a/webpack.mix.js +++ b/webpack.mix.js @@ -22,7 +22,38 @@ mix.webpackConfig({ symlinks: false, alias: { "vue-monaco": path.resolve(__dirname, "resources/js/vue-monaco-amd.js"), - "styles": path.resolve(__dirname, "resources/sass"), + styles: path.resolve(__dirname, "resources/sass"), + }, + }, + optimization: { + splitChunks: { + cacheGroups: { + vueVendor: { + test: /[\\/]node_modules[\\/](vue|vue-router|axios|lodash)[\\/]/, + name: "js/vue-vendor", + chunks: "all", + }, + bootstrapVendor: { + test: /[\\/]node_modules[\\/](bootstrap|jquery|bootstrap-vue|popper.js)[\\/]/, + name: "js/bootstrap-vendor", + chunks: "all", + }, + fortawesomeVendor: { + test: /[\\/]node_modules[\\/](@fortawesome\/fontawesome-free|@fortawesome\/fontawesome-svg-core|@fortawesome\/free-brands-svg-icons|@fortawesome\/free-solid-svg-icons|@fortawesome\/vue-fontawesome)[\\/]/, + name: "js/fortawesome-vendor", + chunks: "all", + }, + modelerVendor: { + test: /[\\/]node_modules[\\/](jointjs|bpmn-moddle|luxon)[\\/]/, + name: "js/modeler-vendor", + chunks: "all", + }, + vendor: { + test: /[\\/]node_modules[\\/]/, + name: "js/vendor", + chunks: "all", + }, + }, }, }, }); @@ -52,7 +83,7 @@ mix "@fortawesome/free-brands-svg-icons", "@fortawesome/free-solid-svg-icons", "@fortawesome/vue-fontawesome", - ]) + ], "public/js") .copy("resources/img/*", "public/img") .copy("resources/img/launchpad-images/*", "public/img/launchpad-images") .copy("resources/img/launchpad-images/icons/*", "public/img/launchpad-images/icons") @@ -133,18 +164,18 @@ mix .js("resources/js/tasks/show.js", "public/js/tasks/show.js") .js("resources/js/notifications/index.js", "public/js/notifications/index.js") - .js('resources/js/inbox-rules/index.js', 'public/js/inbox-rules') - .js('resources/js/inbox-rules/show.js', 'public/js/inbox-rules') + .js("resources/js/inbox-rules/index.js", "public/js/inbox-rules") + .js("resources/js/inbox-rules/show.js", "public/js/inbox-rules") .js("resources/js/admin/devlink/index.js", "public/js/admin/devlink") // Note, that this should go last for the extract to properly put the manifest and vendor in the right location // See: https://github.com/JeffreyWay/laravel-mix/issues/1118 .js("resources/js/app.js", "public/js"); - // .polyfill({ - // enabled: true, - // useBuiltIns: false, - // targets: "> 0.25%, not dead" - // }); +// .polyfill({ +// enabled: true, +// useBuiltIns: false, +// targets: "> 0.25%, not dead" +// }); // Monaco AMD modules. Copy only the files we need to make the build faster. const monacoSource = "node_modules/monaco-editor/min/vs/"; From 489f064fa39791c94a1b7297e1e36236de34092e Mon Sep 17 00:00:00 2001 From: Henry Jonas Date: Fri, 6 Dec 2024 14:19:35 -0400 Subject: [PATCH 02/30] FOUR-20583: S2: Improve vendors --- package.json | 1 + resources/js/empty.js | 16 ++++++++ webpack-vendors.mix.js | 83 ++++++++++++++++++++++++++++++++++++++++++ webpack.mix.js | 33 +---------------- 4 files changed, 101 insertions(+), 32 deletions(-) create mode 100644 resources/js/empty.js create mode 100644 webpack-vendors.mix.js diff --git a/package.json b/package.json index 7ee0065443..714de2e60e 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "postinstall": "mkdir -p public/css/precompiled && cp -rf node_modules/npm-font-open-sans public/css/precompiled/npm-font-open-sans && cp -rf node_modules/bootstrap/scss public/css/precompiled/bootstrap && cp -rf node_modules/@fortawesome/fontawesome-free public/css/precompiled/fontawesome-free && cp -rf node_modules/@fontsource/poppins public/css/precompiled/poppins && cp -rf node_modules/@processmaker/vue-multiselect/dist/vue-multiselect.min.css public/css/precompiled", "dev": "npm run development && mix --mix-config=webpack-login.mix.js", "development": "mix", + "vendors": "mix --mix-config=webpack-vendors.mix.js", "watch": "mix watch", "watch-poll": "mix watch -- --watch-options-poll=1000", "hot": "mix watch --hot", diff --git a/resources/js/empty.js b/resources/js/empty.js new file mode 100644 index 0000000000..3310000ea8 --- /dev/null +++ b/resources/js/empty.js @@ -0,0 +1,16 @@ +import vue from "vue"; +import vueRouter from "vue-router"; +import jquery from "jquery"; +import bootstrapVue from "bootstrap-vue"; +import axios from "axios"; +import popper from "popper.js"; +import lodash from "lodash"; +import bootstrap from "bootstrap"; +import jointjs from "jointjs"; +import luxon from "luxon"; +import bpmnModdle from "bpmn-moddle"; +import fontawesomeFree from "@fortawesome/fontawesome-free"; +import fontawesomeSvgCore from "@fortawesome/fontawesome-svg-core"; +import fontawesomeFreeBrandsSvgIcons from "@fortawesome/free-brands-svg-icons"; +import fontawesomeFreeSolidSvgIcons from "@fortawesome/free-solid-svg-icons"; +import fontawesomeVueFontawesome from "@fortawesome/vue-fontawesome"; diff --git a/webpack-vendors.mix.js b/webpack-vendors.mix.js new file mode 100644 index 0000000000..a70e104b29 --- /dev/null +++ b/webpack-vendors.mix.js @@ -0,0 +1,83 @@ +const mix = require("laravel-mix"); +const path = require("path"); +const fs = require("fs"); + +const manifestPath = path.resolve(__dirname, "public/mix-manifest.json"); +let existingContent = {}; + +require("laravel-mix-polyfill"); + +/* + |-------------------------------------------------------------------------- + | Mix Asset Management + |-------------------------------------------------------------------------- + | + | Mix provides a clean, fluent API for defining some Webpack build steps + | for your Laravel application. By default, we are compiling the Sass + | file for the application as well as bundling up all the JS files. + | +*/ + +mix.webpackConfig({ + plugins: [], + externals: [], + resolve: { + extensions: [".*", ".js", ".ts", ".mjs", ".vue", ".json"], + symlinks: false, + alias: { + styles: path.resolve(__dirname, "resources/sass"), + }, + }, +}); + +mix.options({ + legacyNodePolyfills: false, + terser: { + parallel: true, + }, +}); + +mix.extract([ + "jquery", + "bootstrap-vue", + "popper.js", + "bootstrap", +], "public/js/bootstrap-vendor.js") + .extract([ + "@fortawesome/fontawesome-free", + "@fortawesome/fontawesome-svg-core", + "@fortawesome/free-brands-svg-icons", + "@fortawesome/free-solid-svg-icons", + "@fortawesome/vue-fontawesome", + ], "public/js/fortawesome-vendor.js") + .extract([ + "jointjs", + "luxon", + "bpmn-moddle", + ], "public/js/modeler-vendor.js") + .extract([ + "vue", + "vue-router", + "axios", + "lodash", + ], "public/js/vue-vendor.js") + .js("resources/js/empty.js", "public/js") + .version(); + +mix.vue({ version: 2 }) + .before(() => { + // Check if the manifest file already exists and get the current content + if (fs.existsSync(manifestPath)) { + existingContent = JSON.parse(fs.readFileSync(manifestPath, "utf8")); + } + }) + .then(() => { + // Reload the generated manifest content + const newContent = JSON.parse(fs.readFileSync(manifestPath, "utf8")); + + // Merge the existing content with the newly generated content + const mergedContent = { ...existingContent, ...newContent }; + + // Output the result as formatted JSON + fs.writeFileSync(manifestPath, JSON.stringify(mergedContent, null, 4)); + }); diff --git a/webpack.mix.js b/webpack.mix.js index e4915f4519..e86f55045a 100644 --- a/webpack.mix.js +++ b/webpack.mix.js @@ -25,37 +25,6 @@ mix.webpackConfig({ styles: path.resolve(__dirname, "resources/sass"), }, }, - optimization: { - splitChunks: { - cacheGroups: { - vueVendor: { - test: /[\\/]node_modules[\\/](vue|vue-router|axios|lodash)[\\/]/, - name: "js/vue-vendor", - chunks: "all", - }, - bootstrapVendor: { - test: /[\\/]node_modules[\\/](bootstrap|jquery|bootstrap-vue|popper.js)[\\/]/, - name: "js/bootstrap-vendor", - chunks: "all", - }, - fortawesomeVendor: { - test: /[\\/]node_modules[\\/](@fortawesome\/fontawesome-free|@fortawesome\/fontawesome-svg-core|@fortawesome\/free-brands-svg-icons|@fortawesome\/free-solid-svg-icons|@fortawesome\/vue-fontawesome)[\\/]/, - name: "js/fortawesome-vendor", - chunks: "all", - }, - modelerVendor: { - test: /[\\/]node_modules[\\/](jointjs|bpmn-moddle|luxon)[\\/]/, - name: "js/modeler-vendor", - chunks: "all", - }, - vendor: { - test: /[\\/]node_modules[\\/]/, - name: "js/vendor", - chunks: "all", - }, - }, - }, - }, }); mix.options({ @@ -83,7 +52,7 @@ mix "@fortawesome/free-brands-svg-icons", "@fortawesome/free-solid-svg-icons", "@fortawesome/vue-fontawesome", - ], "public/js") + ]) .copy("resources/img/*", "public/img") .copy("resources/img/launchpad-images/*", "public/img/launchpad-images") .copy("resources/img/launchpad-images/icons/*", "public/img/launchpad-images/icons") From 189442ccde8e6fa21a4da6d8314b2c2ac0755528 Mon Sep 17 00:00:00 2001 From: Henry Jonas Date: Fri, 6 Dec 2024 14:55:01 -0400 Subject: [PATCH 03/30] FOUR-20583: S2: Improve vendors --- resources/js/empty.js | 16 ---- .../views/auth/passwords/change.blade.php | 5 +- .../views/layouts/ai-qr-mobile.blade.php | 5 +- resources/views/layouts/layout.blade.php | 5 +- resources/views/layouts/preview.blade.php | 5 +- resources/views/layouts/print.blade.php | 5 +- resources/views/layouts/process-map.blade.php | 5 +- resources/views/tasks/preview.blade.php | 5 +- webpack-vendors.mix.js | 83 ------------------- webpack.mix.js | 22 +++-- 10 files changed, 42 insertions(+), 114 deletions(-) delete mode 100644 resources/js/empty.js delete mode 100644 webpack-vendors.mix.js diff --git a/resources/js/empty.js b/resources/js/empty.js deleted file mode 100644 index 3310000ea8..0000000000 --- a/resources/js/empty.js +++ /dev/null @@ -1,16 +0,0 @@ -import vue from "vue"; -import vueRouter from "vue-router"; -import jquery from "jquery"; -import bootstrapVue from "bootstrap-vue"; -import axios from "axios"; -import popper from "popper.js"; -import lodash from "lodash"; -import bootstrap from "bootstrap"; -import jointjs from "jointjs"; -import luxon from "luxon"; -import bpmnModdle from "bpmn-moddle"; -import fontawesomeFree from "@fortawesome/fontawesome-free"; -import fontawesomeSvgCore from "@fortawesome/fontawesome-svg-core"; -import fontawesomeFreeBrandsSvgIcons from "@fortawesome/free-brands-svg-icons"; -import fontawesomeFreeSolidSvgIcons from "@fortawesome/free-solid-svg-icons"; -import fontawesomeVueFontawesome from "@fortawesome/vue-fontawesome"; diff --git a/resources/views/auth/passwords/change.blade.php b/resources/views/auth/passwords/change.blade.php index 1410c0d3dc..d126b301cb 100644 --- a/resources/views/auth/passwords/change.blade.php +++ b/resources/views/auth/passwords/change.blade.php @@ -67,7 +67,10 @@ @section('js') - + + + + @endif - + + + + @endif - + + + + @endif - + + + + @endif - + + + + @endif - + + + + @endif - + + + + '; + $tag .= ''; + $content = str_replace('', $tag . "\n", $content); + $response->setContent($content); + + return $response; + } +} + diff --git a/resources/js/next/config/accesibility.js b/resources/js/next/config/accesibility.js new file mode 100644 index 0000000000..7d743f2eaf --- /dev/null +++ b/resources/js/next/config/accesibility.js @@ -0,0 +1,3 @@ +import AccessibilityMixin from "./components/common/mixins/accessibility"; + +window.Vue.mixin(AccessibilityMixin); diff --git a/resources/js/next/config/i18n.js b/resources/js/next/config/i18n.js new file mode 100644 index 0000000000..6f48f005f6 --- /dev/null +++ b/resources/js/next/config/i18n.js @@ -0,0 +1,41 @@ +import i18next from "i18next"; +import Backend from "i18next-chained-backend"; +import LocalStorageBackend from "i18next-localstorage-backend"; +import XHR from "i18next-xhr-backend"; +import VueI18Next from "@panter/vue-i18next"; + +window.Vue.use(VueI18Next); +window.Vue.mixin({ i18n: new VueI18Next(i18next) }); + +let translationsLoaded = false; +const mdates = JSON.parse( + document.head.querySelector("meta[name=\"i18n-mdate\"]")?.content, +); + +window.ProcessMaker.i18n = i18next; +window.ProcessMaker.i18nPromise = i18next.use(Backend).init({ + lng: document.documentElement.lang, + fallbackLng: "en", // default language when no translations + returnEmptyString: false, // When a translation is an empty string, return the default language, not empty + nsSeparator: false, + keySeparator: false, + parseMissingKeyHandler(value) { + if (!translationsLoaded) { return value; } + // Report that a translation is missing + window.ProcessMaker.missingTranslation(value); + // Fallback to showing the english version + return value; + }, + backend: { + backends: [ + LocalStorageBackend, // Try cache first + XHR, + ], + backendOptions: [ + { versions: mdates }, + { loadPath: "/i18next/fetch/{{lng}}/_default" }, + ], + }, +}); + +window.ProcessMaker.i18nPromise.then(() => { translationsLoaded = true; }); diff --git a/resources/js/next/config/modals.js b/resources/js/next/config/modals.js new file mode 100644 index 0000000000..52c8a40aa0 --- /dev/null +++ b/resources/js/next/config/modals.js @@ -0,0 +1,34 @@ + +// Setup our login modal +window.ProcessMaker.sessionModal = function (title, message, time, warnSeconds) { + ProcessMaker.navbar.sessionTitle = title || __("Session Warning"); + ProcessMaker.navbar.sessionMessage = message || __("Your session is about to expire."); + ProcessMaker.navbar.sessionTime = time; + ProcessMaker.navbar.sessionWarnSeconds = warnSeconds; + ProcessMaker.navbar.sessionShow = true; +}; + +window.ProcessMaker.closeSessionModal = function () { + ProcessMaker.navbar.sessionShow = false; +}; + +// Set out own specific confirm modal. +window.ProcessMaker.confirmModal = function (title, message, variant, callback, size = "md", dataTestClose = "confirm-btn-close", dataTestOk = "confirm-btn-ok") { + ProcessMaker.navbar.confirmTitle = title || __("Confirm"); + ProcessMaker.navbar.confirmMessage = message || __("Are you sure you want to delete?"); + ProcessMaker.navbar.confirmVariant = variant; + ProcessMaker.navbar.confirmCallback = callback; + ProcessMaker.navbar.confirmShow = true; + ProcessMaker.navbar.confirmSize = size; + ProcessMaker.navbar.confirmDataTestClose = dataTestClose; + ProcessMaker.navbar.confirmDataTestOk = dataTestOk; +}; + +// Set out own specific message modal. +window.ProcessMaker.messageModal = function (title, message, variant, callback) { + ProcessMaker.navbar.messageTitle = title || __("Message"); + ProcessMaker.navbar.messageMessage = message || __(""); + ProcessMaker.navbar.messageVariant = variant; + ProcessMaker.navbar.messageCallback = callback; + ProcessMaker.navbar.messageShow = true; +}; \ No newline at end of file diff --git a/resources/js/next/config/momentConfig.js b/resources/js/next/config/momentConfig.js new file mode 100644 index 0000000000..673fee09fc --- /dev/null +++ b/resources/js/next/config/momentConfig.js @@ -0,0 +1,11 @@ +import moment from "moment-timezone"; + +if (window.ProcessMaker && window.ProcessMaker.user) { + moment.tz.setDefault(window.ProcessMaker.user.timezone); + moment.defaultFormat = window.ProcessMaker.user.datetime_format; + moment.defaultFormatUtc = window.ProcessMaker.user.datetime_format; +} +if (document.documentElement.lang) { + moment.locale(document.documentElement.lang); + window.ProcessMaker.user.lang = document.documentElement.lang; +} diff --git a/resources/js/next/config/notifications.js b/resources/js/next/config/notifications.js new file mode 100644 index 0000000000..8fc00a9688 --- /dev/null +++ b/resources/js/next/config/notifications.js @@ -0,0 +1,66 @@ +const isProd = document.head.querySelector("meta[name=\"is-prod\"]")?.content === "true"; + +window.ProcessMaker = Object.assign(window.ProcessMaker, { + /** + * ProcessMaker Notifications + */ + notifications: [], + /** + * Push a notification. + * + * @param {object} notification + * + * @returns {void} + */ + pushNotification(notification) { + if (this.notifications.filter((x) => x.id === notification).length === 0) { + this.notifications.push(notification); + } + }, + /** + * Removes notifications by message ids or urls + * + * @returns {void} + * @param messageIds + * + * @param urls + */ + removeNotifications(messageIds = [], urls = []) { + return window.ProcessMaker.apiClient.put("/read_notifications", { message_ids: messageIds, routes: urls }).then(() => { + messageIds.forEach((messageId) => { + ProcessMaker.notifications.splice(ProcessMaker.notifications.findIndex((x) => x.id === messageId), 1); + }); + + urls.forEach((url) => { + const messageIndex = ProcessMaker.notifications.findIndex((x) => x.url === url); + if (messageIndex >= 0) { + ProcessMaker.removeNotification(ProcessMaker.notifications[messageIndex].id); + } + }); + }); + }, + /** + * Mark as unread a list of notifications + * + * @returns {void} + * @param messageIds + * + * @param urls + */ + unreadNotifications(messageIds = [], urls = []) { + return window.ProcessMaker.apiClient.put("/unread_notifications", { message_ids: messageIds, routes: urls }); + }, + + missingTranslations: new Set(), + missingTranslation(value) { + if (this.missingTranslations.has(value)) { return; } + this.missingTranslations.add(value); + if (!isProd) { + console.warn("Missing Translation:", value); + } + }, + $notifications: { + icons: {}, + }, +}); + diff --git a/resources/js/next/config/openAI.js b/resources/js/next/config/openAI.js new file mode 100644 index 0000000000..f054720947 --- /dev/null +++ b/resources/js/next/config/openAI.js @@ -0,0 +1,11 @@ +const openAiEnabled = document.head.querySelector("meta[name=\"open-ai-nlq-to-pmql\"]"); + +if (openAiEnabled) { + window.ProcessMaker.openAi = { + enabled: openAiEnabled.content, + }; +} else { + window.ProcessMaker.openAi = { + enabled: false, + }; +} diff --git a/resources/js/next/config/processmaker.js b/resources/js/next/config/processmaker.js new file mode 100644 index 0000000000..29b6890a83 --- /dev/null +++ b/resources/js/next/config/processmaker.js @@ -0,0 +1,134 @@ + +/** + * Create a axios instance which any vue component can bring in to call + * REST api endpoints through oauth authentication + * + */ +window.ProcessMaker.apiClient = require("axios"); + +window.ProcessMaker.apiClient.defaults.headers.common["X-Requested-With"] = "XMLHttpRequest"; + +/** + * Next we will register the CSRF Token as a common header with Axios so that + * all outgoing HTTP requests automatically have it attached. This is just + * a simple convenience so we don't have to attach every token manually. + */ + +const token = document.head.querySelector("meta[name=\"csrf-token\"]"); + + +if (token) { + window.ProcessMaker.apiClient.defaults.headers.common["X-CSRF-TOKEN"] = token.content; +} else { + console.error("CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token"); +} + +// Setup api versions +const apiVersionConfig = [ + { version: "1.0", baseURL: "/api/1.0/" }, + { version: "1.1", baseURL: "/api/1.1/" }, +]; + +window.ProcessMaker.apiClient.defaults.baseURL = apiVersionConfig[0].baseURL; +window.ProcessMaker.apiClient.interceptors.request.use((config) => { + if (typeof config.url !== "string" || !config.url) { + throw new Error("Invalid URL in the request configuration"); + } + + apiVersionConfig.forEach(({ version, baseURL }) => { + const versionPrefix = `/api/${version}/`; + if (config.url.startsWith(versionPrefix)) { + // eslint-disable-next-line no-param-reassign + config.baseURL = baseURL; + // eslint-disable-next-line no-param-reassign + config.url = config.url.replace(versionPrefix, ""); + } + }); + + return config; +}); + +// Set the default API timeout +let apiTimeout = 5000; +if (window.Processmaker && window.Processmaker.apiTimeout !== undefined) { + apiTimeout = window.Processmaker.apiTimeout; +} +window.ProcessMaker.apiClient.defaults.timeout = apiTimeout; + + + +// flags print forms +window.ProcessMaker.apiClient.requestCount = 0; +window.ProcessMaker.apiClient.requestCountFlag = false; + +window.ProcessMaker.apiClient.interceptors.request.use((request) => { + // flags print forms + if (window.ProcessMaker.apiClient.requestCountFlag) { + window.ProcessMaker.apiClient.requestCount++; + } + + window.ProcessMaker.EventBus.$emit("api-client-loading", request); + return request; +}); + +window.ProcessMaker.apiClient.interceptors.response.use((response) => { + // TODO: this could be used to show a default "created/upated/deleted resource" alert + // response.config.method (PUT, POST, DELETE) + // response.config.url (extract resource name) + window.ProcessMaker.EventBus.$emit("api-client-done", response); + // flags print forms + if (window.ProcessMaker.apiClient.requestCountFlag && window.ProcessMaker.apiClient.requestCount > 0) { + window.ProcessMaker.apiClient.requestCount--; + } + return response; +}, (error) => { + // Set in your .catch to false to not show the alert inside window.ProcessMaker.apiClient + if (!error?.response?.showAlert) { + return Promise.reject(error); + } + + if (error.code && error.code === "ERR_CANCELED") { + return Promise.reject(error); + } + window.ProcessMaker.EventBus.$emit("api-client-error", error); + if (error.response && error.response.status && error.response.status === 401) { + // stop 401 error consuming endpoints with data-sources + const { url } = error.config; + if (url.includes("/data_sources/")) { + if (url.includes("requests/") || url.includes("/test")) { + throw error; + } + } + window.location = "/login"; + } else { + if (_.has(error, "config.url") && !error.config.url.match("/debug")) { + window.ProcessMaker.apiClient.post("/debug", { + name: "Javascript ProcessMaker.apiClient Error", + message: JSON.stringify({ + message: error.message, + code: error.code, + config: error.config, + }), + }); + } + return Promise.reject(error); + } +}); + + +// Display any uncaught promise rejections from axios in the Process Maker alert box +window.addEventListener("unhandledrejection", (event) => { + const error = event.reason; + if (error.config && error.config._defaultErrorShown) { + // Already handeled + event.preventDefault(); // stops the unhandled rejection error + } else if (error.response && error.response.data && error.response.data.message) { + if (!(error.code && error.code === "ECONNABORTED")) { + window.ProcessMaker.alert(error.response.data.message, "danger"); + } + } else if (error.message) { + if (!(error.code && error.code === "ECONNABORTED")) { + window.ProcessMaker.alert(error.message, "danger"); + } + } +}); \ No newline at end of file diff --git a/resources/js/next/config/session.js b/resources/js/next/config/session.js new file mode 100644 index 0000000000..bdbba319f3 --- /dev/null +++ b/resources/js/next/config/session.js @@ -0,0 +1,79 @@ +if (window.ProcessMaker.user) { + // Session timeout + const timeoutScript = document.head.querySelector("meta[name=\"timeout-worker\"]")?.content; + window.ProcessMaker.AccountTimeoutLength = parseInt(eval(document.head.querySelector("meta[name=\"timeout-length\"]")?.content)); + window.ProcessMaker.AccountTimeoutWarnSeconds = parseInt(document.head.querySelector("meta[name=\"timeout-warn-seconds\"]")?.content); + window.ProcessMaker.AccountTimeoutEnabled = document.head.querySelector("meta[name=\"timeout-enabled\"]") ? parseInt(document.head.querySelector("meta[name=\"timeout-enabled\"]")?.content) : 1; + window.ProcessMaker.AccountTimeoutWorker = new Worker(timeoutScript); + window.ProcessMaker.AccountTimeoutWorker.addEventListener("message", (e) => { + if (e.data.method === "countdown") { + window.ProcessMaker.sessionModal( + "Session Warning", + "

Your user session is expiring. If your session expires, all of your unsaved data will be lost.

Would you like to stay connected?

", + e.data.data.time, + window.ProcessMaker.AccountTimeoutWarnSeconds, + ); + } + if (e.data.method === "timedOut") { + window.location = "/logout?timeout=true"; + } + }); + + // in some cases it's necessary to start manually + window.ProcessMaker.AccountTimeoutWorker.postMessage({ + method: "start", + data: { + timeout: window.ProcessMaker.AccountTimeoutLength, + warnSeconds: window.ProcessMaker.AccountTimeoutWarnSeconds, + enabled: window.ProcessMaker.AccountTimeoutEnabled, + }, + }); + + const isSameDevice = (e) => { + const localDeviceId = Vue.$cookies.get(e.device_variable); + const remoteDeviceId = e.device_id; + return localDeviceId && localDeviceId === remoteDeviceId; + }; + + window.Echo.private(`ProcessMaker.Models.User.${window.ProcessMaker.user.id}`) + .notification((token) => { + ProcessMaker.pushNotification(token); + }) + .listen(".SessionStarted", (e) => { + const lifetime = parseInt(eval(e.lifetime)); + if (isSameDevice(e)) { + window.ProcessMaker.AccountTimeoutWorker.postMessage({ + method: "start", + data: { + timeout: lifetime, + warnSeconds: window.ProcessMaker.AccountTimeoutWarnSeconds, + enabled: window.ProcessMaker.AccountTimeoutEnabled, + }, + }); + if (window.ProcessMaker.closeSessionModal) { + window.ProcessMaker.closeSessionModal(); + } + } + }) + .listen(".Logout", (e) => { + if (isSameDevice(e) && window.location.pathname.indexOf("/logout") === -1) { + const localDeviceId = Vue.$cookies.get(e.device_variable); + const redirectLogoutinterval = setInterval(() => { + const newDeviceId = Vue.$cookies.get(e.device_variable); + if (localDeviceId !== newDeviceId) { + clearInterval(redirectLogoutinterval); + window.location.href = "/logout"; + } + }, 100); + } + }) + .listen(".SecurityLogDownloadJobCompleted", (e) => { + if (e.success) { + const { link } = e; + const { message } = e; + window.ProcessMaker.alert(message, "success", 0, false, false, link); + } else { + window.ProcessMaker.alert(e.message, "warning"); + } + }); +} diff --git a/resources/js/next/config/user.js b/resources/js/next/config/user.js new file mode 100644 index 0000000000..f21a9b8b9e --- /dev/null +++ b/resources/js/next/config/user.js @@ -0,0 +1,31 @@ +import datetime_format from "../../data/datetime_formats.json"; + +const userID = document.head.querySelector("meta[name=\"user-id\"]"); +const userFullName = document.head.querySelector("meta[name=\"user-full-name\"]"); +const userAvatar = document.head.querySelector("meta[name=\"user-avatar\"]"); +const formatDate = document.head.querySelector("meta[name=\"datetime-format\"]"); +const timezone = document.head.querySelector("meta[name=\"timezone\"]"); +const appUrl = document.head.querySelector("meta[name=\"app-url\"]"); + +if (appUrl) { + window.ProcessMaker.app = { + url: appUrl.content, + }; +} + +if (userID) { + window.ProcessMaker.user = { + id: userID.content, + datetime_format: formatDate?.content, + calendar_format: formatDate?.content, + timezone: timezone?.content, + fullName: userFullName?.content, + avatar: userAvatar?.content, + }; + datetime_format.forEach((value) => { + if (formatDate.content === value.format) { + window.ProcessMaker.user.datetime_format = value.momentFormat; + window.ProcessMaker.user.calendar_format = value.calendarFormat; + } + }); +} diff --git a/resources/js/next/layout/navbar.js b/resources/js/next/layout/navbar.js new file mode 100644 index 0000000000..2d23571bef --- /dev/null +++ b/resources/js/next/layout/navbar.js @@ -0,0 +1,212 @@ +import newRequestModal from "../../components/requests/requestModal.vue"; +import requestModal from "../../components/requests/modal.vue"; +import notifications from "../../notifications/components/notifications.vue"; +import sessionModal from "../../components/Session.vue"; +import ConfirmationModal from "../../components/Confirm.vue"; +import MessageModal from "../../components/Message.vue"; +import NavbarProfile from "../../components/NavbarProfile.vue"; +import Menu from "../../components/Menu.vue"; + +window.Vue.component("LanguageSelectorButton", (resolve) => { + if (window.ProcessMaker.languageSelectorButtonComponent) { + resolve(window.ProcessMaker.languageSelectorButtonComponent); + } else { + window.ProcessMaker.languageSelectorButtonComponentResolve = resolve; + } +}); + +// Verify if is mobile +const browser = navigator.userAgent; +const isMobileDevice = /Android|webOS|iPhone|iPad|iPod|BlackBerry|Windows Phone/i.test(browser); +window.ProcessMaker.mobileApp = false; +if (isMobileDevice) { + window.ProcessMaker.mobileApp = true; +} + +// Verify is in mobile mode +const isMobileNavbar = window.ProcessMaker.events.$cookies.get("isMobile"); + +// Default alert functionality +window.ProcessMaker.alert = function (text, variant) { + if (typeof text === "string") { + window.alert(text); + } +}; + +// Set our own specific alert function at the ProcessMaker global object that could +// potentially be overwritten by some custom theme support +window.ProcessMaker.alert = function (msg, variant, showValue = 5, stayNextScreen = false, showLoader = false, msgLink = "", msgTitle = "") { + if (showValue === 0) { + // Just show it indefinitely, no countdown + showValue = true; + } + // amount of items allowed in array + if (ProcessMaker.navbar.alerts.length > 5) { + ProcessMaker.navbar.alerts.shift(); + } + ProcessMaker.navbar.alerts.push({ + alertText: msg, + alertLink: msgLink, + alertTitle: msgTitle, + alertShow: showValue, + alertVariant: String(variant), + showLoader, + stayNextScreen, + timestamp: Date.now(), + }); +}; + +// Assign our navbar component to our global ProcessMaker object +window.ProcessMaker.navbar = new Vue({ + el: "#navbar", + components: { + TopMenu: Menu, + requestModal, + notifications, + sessionModal, + ConfirmationModal, + MessageModal, + NavbarProfile, + newRequestModal, + GlobalSearch: (resolve) => { + if (window.ProcessMaker.globalSearchComponent) { + resolve(window.ProcessMaker.globalSearchComponent); + } else { + window.ProcessMaker.globalSearchComponentResolve = resolve; + } + }, + }, + data() { + return { + screenBuilder: null, + messages: ProcessMaker.notifications, + alerts: this.loadLocalAlerts(), + confirmTitle: "", + confirmMessage: "", + confirmVariant: "", + confirmCallback: "", + confirmSize: "md", + confirmDataTestClose: "confirm-btn-close", + confirmDataTestOk: "confirm-btn-ok", + messageTitle: "", + messageMessage: "", + messageVariant: "", + messageCallback: "", + confirmShow: false, + sessionShow: false, + messageShow: false, + sessionTitle: "", + sessionMessage: "", + sessionTime: "", + sessionWarnSeconds: "", + taskTitle: "", + isMobile: false, + isMobileDevice: window.ProcessMaker.mobileApp, + }; + }, + watch: { + alerts(array) { + this.saveLocalAlerts(array); + }, + }, + mounted() { + Vue.nextTick() // This is needed to override the default alert method. + .then(() => { + this.onResize(); + window.addEventListener("resize", this.onResize, { passive: true }); + + if (document.querySelector("meta[name='alert']")) { + ProcessMaker.alert( + document.querySelector("meta[name='alertMessage']").getAttribute("content"), + document.querySelector("meta[name='alertVariant']").getAttribute("content"), + ); + } + const findSB = setInterval(() => { + this.screenBuilder = window.ProcessMaker.ScreenBuilder; + if (this.screenBuilder) { + clearInterval(findSB); + } + }, 80); + }); + }, + methods: { + alertDownChanged(dismissCountDown, item) { + item.alertShow = dismissCountDown; + this.saveLocalAlerts(this.alerts); + }, + alertDismissed(alert) { + alert.alertShow = 0; + const index = this.alerts.indexOf(alert); + let copy = _.cloneDeep(this.alerts); + index > -1 ? copy.splice(index, 1) : null; + // remove old alerts + copy = copy.filter((item) => ((Date.now() - item.timestamp) / 1000) < item.alertShow); + this.saveLocalAlerts(copy); + }, + loadLocalAlerts() { + try { + return window.localStorage.processmakerAlerts + && window.localStorage.processmakerAlerts.substr(0, 1) === "[" + ? JSON.parse(window.localStorage.processmakerAlerts) : []; + } catch (e) { + return []; + } + }, + saveLocalAlerts(array) { + const nextScreenAlerts = array.filter((alert) => alert.stayNextScreen); + window.localStorage.processmakerAlerts = JSON.stringify(nextScreenAlerts); + }, + switchToMobile() { + this.$cookies.set("isMobile", true); + window.open("/requests", "_self"); + }, + getRoutes() { + if (this.$refs.breadcrumbs) { + return this.$refs.breadcrumbs.list; + } + return []; + }, + setRoutes(routes) { + if (this.$refs.breadcrumbs) { + return this.$refs.breadcrumbs.updateRoutes(routes); + } + return false; + }, + onResize() { + this.isMobile = window.innerWidth < 992; + }, + }, +}); + +// Assign our navbar component to our global ProcessMaker object +if (isMobileNavbar === "true") { + window.ProcessMaker.navbarMobile = new Vue({ + el: "#navbarMobile", + components: { + requestModalMobile, + WelcomeModal, + }, + data() { + return { + display: true, + }; + }, + mounted() { + if (this.$cookies.get("firstMounted") === "true") { + $("#welcomeModal").modal("show"); + } + }, + methods: { + switchToDesktop() { + this.$cookies.set("isMobile", false); + window.location.reload(); + }, + onResize() { + this.isMobile = window.innerWidth < 992; + }, + }, + }); +} + +// Breadcrumbs are now part of the navbar component. Alias it here. +window.ProcessMaker.breadcrumbs = window.ProcessMaker.navbar; diff --git a/resources/js/next/layout/sidebar.js b/resources/js/next/layout/sidebar.js new file mode 100644 index 0000000000..7d496a373e --- /dev/null +++ b/resources/js/next/layout/sidebar.js @@ -0,0 +1,31 @@ +import { sanitizeUrl } from "@braintree/sanitize-url"; +// import VueHtml2Canvas from "vue-html2canvas"; +import Sidebaricon from "../../components/Sidebaricon.vue"; + +const { Vue } = window; + +// Vue.use(VueHtml2Canvas); +Vue.prototype.$sanitize = sanitizeUrl; + +Vue.component("LanguageSelectorButton", (resolve) => { + if (window.ProcessMaker.languageSelectorButtonComponent) { + resolve(window.ProcessMaker.languageSelectorButtonComponent); + } else { + window.ProcessMaker.languageSelectorButtonComponentResolve = resolve; + } +}); + +new Vue({ + el: "#sidebar", + components: { + Sidebaricon, + }, + data() { + return { + expanded: false, + }; + }, + created() { + this.expanded === false; + }, +}); diff --git a/resources/js/next/libraries/bootstrap.js b/resources/js/next/libraries/bootstrap.js new file mode 100644 index 0000000000..44bd5e25ee --- /dev/null +++ b/resources/js/next/libraries/bootstrap.js @@ -0,0 +1,4 @@ +import("bootstrap-vue/dist/bootstrap-vue.css"); +import("bootstrap").then((bootstrap) => { + window.bootstrap = bootstrap; +}); diff --git a/resources/js/next/libraries/broadcast.js b/resources/js/next/libraries/broadcast.js new file mode 100644 index 0000000000..17dabd94a7 --- /dev/null +++ b/resources/js/next/libraries/broadcast.js @@ -0,0 +1,12 @@ +import Echo from "laravel-echo"; + +if (window.Processmaker && window.Processmaker.broadcasting) { + const config = window.Processmaker.broadcasting; + + if (config.broadcaster == "pusher") { + window.Pusher = require("pusher-js"); + window.Pusher.logToConsole = config.debug; + } + + window.Echo = new Echo(config); +} diff --git a/resources/js/next/libraries/jquery.js b/resources/js/next/libraries/jquery.js new file mode 100644 index 0000000000..9c0a4f29e7 --- /dev/null +++ b/resources/js/next/libraries/jquery.js @@ -0,0 +1 @@ +window.$ = window.jQuery = require("jquery"); \ No newline at end of file diff --git a/resources/js/next/libraries/lodash.js b/resources/js/next/libraries/lodash.js new file mode 100644 index 0000000000..9ccc9f453e --- /dev/null +++ b/resources/js/next/libraries/lodash.js @@ -0,0 +1,3 @@ +import lodash from "lodash"; + +window._ = lodash; diff --git a/resources/js/next/libraries/popper.js b/resources/js/next/libraries/popper.js new file mode 100644 index 0000000000..519a53b597 --- /dev/null +++ b/resources/js/next/libraries/popper.js @@ -0,0 +1 @@ +window.Popper = require("popper.js").default; diff --git a/resources/js/next/libraries/vueCookies.js b/resources/js/next/libraries/vueCookies.js new file mode 100644 index 0000000000..4280af811f --- /dev/null +++ b/resources/js/next/libraries/vueCookies.js @@ -0,0 +1,3 @@ +import VueCookies from "vue-cookies"; + +window.Vue.use(VueCookies); diff --git a/resources/js/next/libraries/vueDeepset.js b/resources/js/next/libraries/vueDeepset.js new file mode 100644 index 0000000000..931acd4b9e --- /dev/null +++ b/resources/js/next/libraries/vueDeepset.js @@ -0,0 +1,3 @@ +import * as VueDeepSet from "vue-deepset"; + +window.Vue.use(VueDeepSet); diff --git a/resources/js/next/libraries/vueFormElements.js b/resources/js/next/libraries/vueFormElements.js new file mode 100644 index 0000000000..5276a0d411 --- /dev/null +++ b/resources/js/next/libraries/vueFormElements.js @@ -0,0 +1,3 @@ +import("@processmaker/vue-form-elements").then((vueforms) => { + window.VueFormElements = vueforms; +}); diff --git a/resources/js/next/libraries/vueRouter.js b/resources/js/next/libraries/vueRouter.js new file mode 100644 index 0000000000..eac8c1ef74 --- /dev/null +++ b/resources/js/next/libraries/vueRouter.js @@ -0,0 +1,11 @@ +import Router from "vue-router"; + +window.VueRouter = Router; + +// if (!document.head.querySelector("meta[name=\"is-horizon\"]")) { +window.Vue.use(Router); +// } + +window.ProcessMaker.Router = new Router({ + mode: "history", +}); diff --git a/resources/js/next/libraries/vueTable.js b/resources/js/next/libraries/vueTable.js new file mode 100644 index 0000000000..a88c2fb50b --- /dev/null +++ b/resources/js/next/libraries/vueTable.js @@ -0,0 +1,3 @@ +import { install as VuetableInstall } from "vuetable-2"; + +VuetableInstall(window.Vue); diff --git a/resources/js/next/libraries/vuex.js b/resources/js/next/libraries/vuex.js new file mode 100644 index 0000000000..d0450f0e4c --- /dev/null +++ b/resources/js/next/libraries/vuex.js @@ -0,0 +1,5 @@ +import Vuex from "vuex"; +import GlobalStore from "../../globalStore"; + +window.Vue.use(Vuex); +window.Vue.use(GlobalStore); diff --git a/resources/js/next/modeler.js b/resources/js/next/modeler.js new file mode 100644 index 0000000000..4a8f69bac5 --- /dev/null +++ b/resources/js/next/modeler.js @@ -0,0 +1,6 @@ +window.Modeler = require("@processmaker/modeler"); + +window.ProcessMaker.nodeTypes = []; +window.ProcessMaker.nodeTypes.get = function (id) { + return this.find((node) => node.id === id); +}; diff --git a/resources/js/next/monaco.js b/resources/js/next/monaco.js new file mode 100644 index 0000000000..2d07e7170f --- /dev/null +++ b/resources/js/next/monaco.js @@ -0,0 +1,5 @@ +import MonacoEditor from "vue-monaco"; + +window.VueMonaco = require("vue-monaco"); + +window.Vue.component("monaco-editor", MonacoEditor); diff --git a/resources/js/next/screenBuilder.js b/resources/js/next/screenBuilder.js new file mode 100644 index 0000000000..d94bbacab1 --- /dev/null +++ b/resources/js/next/screenBuilder.js @@ -0,0 +1,48 @@ +const addScriptsToDOM = async function(scripts) { + for (const script of scripts) { + await new Promise((resolve, reject) => { + const scriptElement = document.createElement('script'); + scriptElement.src = script; + scriptElement.async = false; + scriptElement.onload = resolve; + scriptElement.onerror = reject; + document.head.appendChild(scriptElement); + }); + } +}; + +window.Vue.component('VueFormRenderer', function (resolve, reject) { + console.log("VUE FORM RENDERER") + import("@processmaker/screen-builder/dist/vue-form-builder.css"); + + if(screenBuilderScripts){ + console.log("SCREEN BUILDER SCRIPTS 7777", screenBuilderScripts) + addScriptsToDOM(screenBuilderScripts).then(()=>{ + import("@processmaker/screen-builder").then((ScreenBuilder)=>{ + window.Vue.use(ScreenBuilder.default); + window.ScreenBuilder = ScreenBuilder; + + const { initializeScreenCache } = ScreenBuilder; + // Configuration Global object used by ScreenBuilder + // @link https://processmaker.atlassian.net/browse/FOUR-6833 Cache configuration + const screenCacheEnabled = document.head.querySelector("meta[name=\"screen-cache-enabled\"]")?.content ?? "false"; + const screenCacheTimeout = document.head.querySelector("meta[name=\"screen-cache-timeout\"]")?.content ?? "5000"; + window.ProcessMaker.screen = { + cacheEnabled: screenCacheEnabled === "true", + cacheTimeout: Number(screenCacheTimeout), + }; + // Initialize screen-builder cache + initializeScreenCache(window.ProcessMaker.apiClient, window.ProcessMaker.screen); + console.log("VUE FORM RESOLVE") + resolve(ScreenBuilder.VueFormRenderer); + }).catch(reject); + }) + } +}); + + + + + + + diff --git a/resources/jscomposition/cases/casesMain/loader.js b/resources/jscomposition/cases/casesMain/loader.js new file mode 100644 index 0000000000..e3dab5f006 --- /dev/null +++ b/resources/jscomposition/cases/casesMain/loader.js @@ -0,0 +1,49 @@ +import Vue from "vue"; +import * as vue from "vue"; +import { BootstrapVue, BootstrapVueIcons } from "bootstrap-vue"; +import moment from "moment-timezone"; + +import AvatarImage from "../../../js/components/AvatarImage.vue"; +import TimelineItem from "../../../js/components/TimelineItem.vue"; +import SelectUserGroup from "../../../js/components/SelectUserGroup.vue"; + +window.Vue = Vue; +window.vue = vue; +window.moment = moment; + +window.Vue.prototype.moment = moment; +window.Vue.use(BootstrapVue); +window.Vue.use(BootstrapVueIcons); + +import("../../../js/next/libraries/broadcast"); +import("../../../js/next/libraries/vuex"); +import("../../../js/next/libraries/bootstrap"); +import("../../../js/next/libraries/jquery"); +import("../../../js/next/libraries/vueRouter"); +import("../../../js/next/libraries/vueCookies"); +import("../../../js/next/libraries/vueFormElements"); + +window.ProcessMaker = { + EventBus: new Vue(), + events: new Vue(), +}; + +import("../../../js/next/config/processmaker"); +import("../../../js/next/config/i18n"); +import("../../../js/next/config/user"); +import("../../../js/next/config/session"); +import("../../../js/next/config/momentConfig"); +import("../../../js/next/config/notifications"); +import("../../../js/next/config/modals"); +import("../../../js/next/config/openAI"); + +// Screen builder +// import("../../../js/nextDependencies/screenBuilder"); + +// Load libraries dependencies first +import("../../../js/next/layout/sidebar"); +import("../../../js/next/layout/navbar"); + +Vue.component("AvatarImage", AvatarImage); +Vue.component("TimelineItem", TimelineItem); +Vue.component("SelectUserGroup", SelectUserGroup); diff --git a/resources/views/cases/casesMain.blade.php b/resources/views/cases/casesMain.blade.php index dd56200b0c..238fa2696b 100644 --- a/resources/views/cases/casesMain.blade.php +++ b/resources/views/cases/casesMain.blade.php @@ -1,4 +1,4 @@ -@extends('layouts.layout',['content_margin' => '', 'overflow-auto' => '']) +@extends('layouts.layoutnext',['content_margin' => '', 'overflow-auto' => '']) @section('title') {{ __('Cases') }} @@ -13,8 +13,15 @@ @endsection @section('js') - + + + + + @endsection diff --git a/resources/views/layouts/layout.blade.php b/resources/views/layouts/layout.blade.php index 61e7c3c566..01367e08c2 100644 --- a/resources/views/layouts/layout.blade.php +++ b/resources/views/layouts/layout.blade.php @@ -129,9 +129,9 @@ class="main flex-grow-1 h-100 @endif - + + @isset($addons) + + @foreach ($addons as $addon) + @if (!empty($addon['script'])) + {!! $addon['script'] !!} + @endif + @endforeach + @endisset + @if (config('global_header')) + + {!! config('global_header') !!} + + @endif + + +@{{ __('Skip to Content') }} +
+ @if (shouldShow('leftSideBar')) + + @else + + @endif +
+
+ @include('layouts.navbar') +
+
+
+ @yield('content') +
+
+
+
+
+
+

@{{__('Sorry! API failed to load')}}

+

@{{__('Something went wrong. Try refreshing the application')}}

+
+
+ +@if(config('broadcasting.default') == 'redis') + +@endif +{{-- --}} + + @yield('js') + + + + + {{-- @isset($addons) + @foreach ($addons as $addon) + @if (!empty($addon['script_mix'])) + + @endif + @if (!empty($addon['script_mix_module'])) + + @endif + @endforeach + @endisset + + @if (hasPackage('package-accessibility')) + @include('package-accessibility::userway') + @endif --}} + + + diff --git a/webpack.mix.js b/webpack.mix.js index d7d7a24e6c..9c8afbf90e 100644 --- a/webpack.mix.js +++ b/webpack.mix.js @@ -145,6 +145,7 @@ mix // Note, that this should go last for the extract to properly put the manifest and vendor in the right location // See: https://github.com/JeffreyWay/laravel-mix/issues/1118 + .js("resources/jscomposition/cases/casesMain/loader.js", "public/js/composition/cases/casesMain") .js("resources/js/app.js", "public/js"); // .polyfill({ // enabled: true, From 4bdfeb86773a0ee48b8c3c47cef35f5e4ad6d6df Mon Sep 17 00:00:00 2001 From: Henry Jonas Date: Mon, 9 Dec 2024 14:04:01 -0400 Subject: [PATCH 06/30] FOUR-20583-A: Cases Main change to new layout --- resources/js/next/config/accesibility.js | 5 +- resources/js/next/config/i18n.js | 30 ++++-- resources/js/next/config/modals.js | 34 ------- resources/js/next/config/momentConfig.js | 16 +-- resources/js/next/config/notifications.js | 90 ++++++----------- resources/js/next/config/openAI.js | 16 ++- resources/js/next/config/processmaker.js | 99 ++++++++++--------- resources/js/next/config/session.js | 71 ++++++++----- resources/js/next/config/user.js | 19 ++-- resources/js/next/globalVariables.js | 25 +++++ resources/js/next/layout/navbar.js | 95 +++++++++++++----- resources/js/next/layout/sidebar.js | 3 +- resources/js/next/libraries/bootstrap.js | 8 +- resources/js/next/libraries/broadcast.js | 12 ++- resources/js/next/libraries/jquery.js | 7 +- resources/js/next/libraries/vueCookies.js | 5 +- .../js/next/libraries/vueFormElements.js | 4 +- resources/js/next/libraries/vueRouter.js | 13 +-- resources/js/next/libraries/vuex.js | 7 +- resources/js/next/monaco.js | 8 +- resources/js/next/screenBuilder.js | 43 ++++---- .../jscomposition/cases/casesMain/loader.js | 1 - 22 files changed, 344 insertions(+), 267 deletions(-) delete mode 100644 resources/js/next/config/modals.js create mode 100644 resources/js/next/globalVariables.js diff --git a/resources/js/next/config/accesibility.js b/resources/js/next/config/accesibility.js index 7d743f2eaf..1b502a4815 100644 --- a/resources/js/next/config/accesibility.js +++ b/resources/js/next/config/accesibility.js @@ -1,3 +1,6 @@ import AccessibilityMixin from "./components/common/mixins/accessibility"; +import { getGlobalVariable } from "../globalVariables"; -window.Vue.mixin(AccessibilityMixin); +const Vue = getGlobalVariable("Vue"); + +Vue.mixin(AccessibilityMixin); diff --git a/resources/js/next/config/i18n.js b/resources/js/next/config/i18n.js index 6f48f005f6..06c0cc7880 100644 --- a/resources/js/next/config/i18n.js +++ b/resources/js/next/config/i18n.js @@ -3,17 +3,27 @@ import Backend from "i18next-chained-backend"; import LocalStorageBackend from "i18next-localstorage-backend"; import XHR from "i18next-xhr-backend"; import VueI18Next from "@panter/vue-i18next"; +import { setGlobalPMVariable, getGlobalVariable } from "../globalVariables"; -window.Vue.use(VueI18Next); -window.Vue.mixin({ i18n: new VueI18Next(i18next) }); +const Vue = getGlobalVariable("Vue"); +const isProd = document.head.querySelector("meta[name=\"is-prod\"]")?.content === "true"; let translationsLoaded = false; + const mdates = JSON.parse( document.head.querySelector("meta[name=\"i18n-mdate\"]")?.content, ); -window.ProcessMaker.i18n = i18next; -window.ProcessMaker.i18nPromise = i18next.use(Backend).init({ +const missingTranslations = new Set(); +const missingTranslation = function (value) { + if (missingTranslations.has(value)) { return; } + missingTranslations.add(value); + if (!isProd) { + console.warn("Missing Translation:", value); + } +}; + +const i18nPromise = i18next.use(Backend).init({ lng: document.documentElement.lang, fallbackLng: "en", // default language when no translations returnEmptyString: false, // When a translation is an empty string, return the default language, not empty @@ -22,7 +32,7 @@ window.ProcessMaker.i18nPromise = i18next.use(Backend).init({ parseMissingKeyHandler(value) { if (!translationsLoaded) { return value; } // Report that a translation is missing - window.ProcessMaker.missingTranslation(value); + missingTranslation(value); // Fallback to showing the english version return value; }, @@ -38,4 +48,12 @@ window.ProcessMaker.i18nPromise = i18next.use(Backend).init({ }, }); -window.ProcessMaker.i18nPromise.then(() => { translationsLoaded = true; }); +i18nPromise.then(() => { translationsLoaded = true; }); + +Vue.use(VueI18Next); +Vue.mixin({ i18n: new VueI18Next(i18next) }); + +setGlobalPMVariable("i18n", i18next); +setGlobalPMVariable("i18nPromise", i18nPromise); +setGlobalPMVariable("missingTranslations", missingTranslations); +setGlobalPMVariable("missingTranslation", missingTranslation); diff --git a/resources/js/next/config/modals.js b/resources/js/next/config/modals.js deleted file mode 100644 index 52c8a40aa0..0000000000 --- a/resources/js/next/config/modals.js +++ /dev/null @@ -1,34 +0,0 @@ - -// Setup our login modal -window.ProcessMaker.sessionModal = function (title, message, time, warnSeconds) { - ProcessMaker.navbar.sessionTitle = title || __("Session Warning"); - ProcessMaker.navbar.sessionMessage = message || __("Your session is about to expire."); - ProcessMaker.navbar.sessionTime = time; - ProcessMaker.navbar.sessionWarnSeconds = warnSeconds; - ProcessMaker.navbar.sessionShow = true; -}; - -window.ProcessMaker.closeSessionModal = function () { - ProcessMaker.navbar.sessionShow = false; -}; - -// Set out own specific confirm modal. -window.ProcessMaker.confirmModal = function (title, message, variant, callback, size = "md", dataTestClose = "confirm-btn-close", dataTestOk = "confirm-btn-ok") { - ProcessMaker.navbar.confirmTitle = title || __("Confirm"); - ProcessMaker.navbar.confirmMessage = message || __("Are you sure you want to delete?"); - ProcessMaker.navbar.confirmVariant = variant; - ProcessMaker.navbar.confirmCallback = callback; - ProcessMaker.navbar.confirmShow = true; - ProcessMaker.navbar.confirmSize = size; - ProcessMaker.navbar.confirmDataTestClose = dataTestClose; - ProcessMaker.navbar.confirmDataTestOk = dataTestOk; -}; - -// Set out own specific message modal. -window.ProcessMaker.messageModal = function (title, message, variant, callback) { - ProcessMaker.navbar.messageTitle = title || __("Message"); - ProcessMaker.navbar.messageMessage = message || __(""); - ProcessMaker.navbar.messageVariant = variant; - ProcessMaker.navbar.messageCallback = callback; - ProcessMaker.navbar.messageShow = true; -}; \ No newline at end of file diff --git a/resources/js/next/config/momentConfig.js b/resources/js/next/config/momentConfig.js index 673fee09fc..abb4ad11e4 100644 --- a/resources/js/next/config/momentConfig.js +++ b/resources/js/next/config/momentConfig.js @@ -1,11 +1,15 @@ -import moment from "moment-timezone"; +import { getGlobalPMVariable, getGlobalVariable } from "../globalVariables"; -if (window.ProcessMaker && window.ProcessMaker.user) { - moment.tz.setDefault(window.ProcessMaker.user.timezone); - moment.defaultFormat = window.ProcessMaker.user.datetime_format; - moment.defaultFormatUtc = window.ProcessMaker.user.datetime_format; +const moment = getGlobalVariable("moment"); +const user = getGlobalPMVariable("user"); + +if (user) { + moment.tz.setDefault(user.timezone); + moment.defaultFormat = user.datetime_format; + moment.defaultFormatUtc = user.datetime_format; } + if (document.documentElement.lang) { moment.locale(document.documentElement.lang); - window.ProcessMaker.user.lang = document.documentElement.lang; + user.lang = document.documentElement.lang; } diff --git a/resources/js/next/config/notifications.js b/resources/js/next/config/notifications.js index 8fc00a9688..7887d1ce00 100644 --- a/resources/js/next/config/notifications.js +++ b/resources/js/next/config/notifications.js @@ -1,66 +1,36 @@ -const isProd = document.head.querySelector("meta[name=\"is-prod\"]")?.content === "true"; +import { getGlobalPMVariable, setGlobalPMVariable } from "../globalVariables"; -window.ProcessMaker = Object.assign(window.ProcessMaker, { - /** - * ProcessMaker Notifications - */ - notifications: [], - /** - * Push a notification. - * - * @param {object} notification - * - * @returns {void} - */ - pushNotification(notification) { - if (this.notifications.filter((x) => x.id === notification).length === 0) { - this.notifications.push(notification); - } - }, - /** - * Removes notifications by message ids or urls - * - * @returns {void} - * @param messageIds - * - * @param urls - */ - removeNotifications(messageIds = [], urls = []) { - return window.ProcessMaker.apiClient.put("/read_notifications", { message_ids: messageIds, routes: urls }).then(() => { - messageIds.forEach((messageId) => { - ProcessMaker.notifications.splice(ProcessMaker.notifications.findIndex((x) => x.id === messageId), 1); - }); +const apiClient = getGlobalPMVariable("apiClient"); + +const notifications = []; + +const pushNotification = (notification) => { + if (notifications.filter((x) => x.id === notification).length === 0) { + notifications.push(notification); + } +}; - urls.forEach((url) => { - const messageIndex = ProcessMaker.notifications.findIndex((x) => x.url === url); - if (messageIndex >= 0) { - ProcessMaker.removeNotification(ProcessMaker.notifications[messageIndex].id); - } - }); - }); - }, - /** - * Mark as unread a list of notifications - * - * @returns {void} - * @param messageIds - * - * @param urls - */ - unreadNotifications(messageIds = [], urls = []) { - return window.ProcessMaker.apiClient.put("/unread_notifications", { message_ids: messageIds, routes: urls }); - }, +const removeNotifications = (messageIds = [], urls = []) => apiClient.put("/read_notifications", { message_ids: messageIds, routes: urls }).then(() => { + messageIds.forEach((messageId) => { + notifications.splice(notifications.findIndex((x) => x.id === messageId), 1); + }); - missingTranslations: new Set(), - missingTranslation(value) { - if (this.missingTranslations.has(value)) { return; } - this.missingTranslations.add(value); - if (!isProd) { - console.warn("Missing Translation:", value); + urls.forEach((url) => { + const messageIndex = notifications.findIndex((x) => x.url === url); + if (messageIndex >= 0) { + removeNotifications(notifications[messageIndex].id); } - }, - $notifications: { - icons: {}, - }, + }); }); +const unreadNotifications = (messageIds = [], urls = []) => apiClient.put("/unread_notifications", { message_ids: messageIds, routes: urls }); + +const $notifications = { + icons: {}, +}; + +setGlobalPMVariable("notifications", notifications); +setGlobalPMVariable("pushNotification", pushNotification); +setGlobalPMVariable("removeNotifications", removeNotifications); +setGlobalPMVariable("unreadNotifications", unreadNotifications); +setGlobalPMVariable("$notifications", $notifications); diff --git a/resources/js/next/config/openAI.js b/resources/js/next/config/openAI.js index f054720947..07bf37d5cd 100644 --- a/resources/js/next/config/openAI.js +++ b/resources/js/next/config/openAI.js @@ -1,11 +1,9 @@ +import { setGlobalPMVariable } from "../globalVariables"; + const openAiEnabled = document.head.querySelector("meta[name=\"open-ai-nlq-to-pmql\"]"); -if (openAiEnabled) { - window.ProcessMaker.openAi = { - enabled: openAiEnabled.content, - }; -} else { - window.ProcessMaker.openAi = { - enabled: false, - }; -} +setGlobalPMVariable("openAiEnabled", openAiEnabled ? { + enabled: openAiEnabled.content, +} : { + enabled: false, +}); diff --git a/resources/js/next/config/processmaker.js b/resources/js/next/config/processmaker.js index 29b6890a83..dba2ef3d43 100644 --- a/resources/js/next/config/processmaker.js +++ b/resources/js/next/config/processmaker.js @@ -1,27 +1,8 @@ - -/** - * Create a axios instance which any vue component can bring in to call - * REST api endpoints through oauth authentication - * - */ -window.ProcessMaker.apiClient = require("axios"); - -window.ProcessMaker.apiClient.defaults.headers.common["X-Requested-With"] = "XMLHttpRequest"; - -/** - * Next we will register the CSRF Token as a common header with Axios so that - * all outgoing HTTP requests automatically have it attached. This is just - * a simple convenience so we don't have to attach every token manually. - */ +import axios from "axios"; +import { setGlobalPMVariable, getGlobalPMVariable } from "../globalVariables"; const token = document.head.querySelector("meta[name=\"csrf-token\"]"); - - -if (token) { - window.ProcessMaker.apiClient.defaults.headers.common["X-CSRF-TOKEN"] = token.content; -} else { - console.error("CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token"); -} +const EventBus = getGlobalPMVariable("EventBus"); // Setup api versions const apiVersionConfig = [ @@ -29,8 +10,21 @@ const apiVersionConfig = [ { version: "1.1", baseURL: "/api/1.1/" }, ]; -window.ProcessMaker.apiClient.defaults.baseURL = apiVersionConfig[0].baseURL; -window.ProcessMaker.apiClient.interceptors.request.use((config) => { +// Set the default API timeout +let apiTimeout = 5000; + +/** + * Create a axios instance which any vue component can bring in to call + * REST api endpoints through oauth authentication + */ + +const apiClient = axios; + +apiClient.defaults.headers.common["X-Requested-With"] = "XMLHttpRequest"; + +apiClient.defaults.baseURL = apiVersionConfig[0].baseURL; + +apiClient.interceptors.request.use((config) => { if (typeof config.url !== "string" || !config.url) { throw new Error("Invalid URL in the request configuration"); } @@ -48,37 +42,27 @@ window.ProcessMaker.apiClient.interceptors.request.use((config) => { return config; }); -// Set the default API timeout -let apiTimeout = 5000; -if (window.Processmaker && window.Processmaker.apiTimeout !== undefined) { - apiTimeout = window.Processmaker.apiTimeout; -} -window.ProcessMaker.apiClient.defaults.timeout = apiTimeout; - - - // flags print forms -window.ProcessMaker.apiClient.requestCount = 0; -window.ProcessMaker.apiClient.requestCountFlag = false; - -window.ProcessMaker.apiClient.interceptors.request.use((request) => { +apiClient.requestCount = 0; +apiClient.requestCountFlag = false; +apiClient.interceptors.request.use((request) => { // flags print forms - if (window.ProcessMaker.apiClient.requestCountFlag) { - window.ProcessMaker.apiClient.requestCount++; + if (apiClient.requestCountFlag) { + apiClient.requestCount += 1; } - window.ProcessMaker.EventBus.$emit("api-client-loading", request); + EventBus.$emit("api-client-loading", request); return request; }); -window.ProcessMaker.apiClient.interceptors.response.use((response) => { +apiClient.interceptors.response.use((response) => { // TODO: this could be used to show a default "created/upated/deleted resource" alert // response.config.method (PUT, POST, DELETE) // response.config.url (extract resource name) - window.ProcessMaker.EventBus.$emit("api-client-done", response); - // flags print forms - if (window.ProcessMaker.apiClient.requestCountFlag && window.ProcessMaker.apiClient.requestCount > 0) { - window.ProcessMaker.apiClient.requestCount--; + EventBus.$emit("api-client-done", response); + + if (apiClient.requestCountFlag && apiClient.requestCount > 0) { + apiClient.requestCount -= 1; } return response; }, (error) => { @@ -90,7 +74,7 @@ window.ProcessMaker.apiClient.interceptors.response.use((response) => { if (error.code && error.code === "ERR_CANCELED") { return Promise.reject(error); } - window.ProcessMaker.EventBus.$emit("api-client-error", error); + EventBus.$emit("api-client-error", error); if (error.response && error.response.status && error.response.status === 401) { // stop 401 error consuming endpoints with data-sources const { url } = error.config; @@ -102,7 +86,7 @@ window.ProcessMaker.apiClient.interceptors.response.use((response) => { window.location = "/login"; } else { if (_.has(error, "config.url") && !error.config.url.match("/debug")) { - window.ProcessMaker.apiClient.post("/debug", { + apiClient.post("/debug", { name: "Javascript ProcessMaker.apiClient Error", message: JSON.stringify({ message: error.message, @@ -115,6 +99,25 @@ window.ProcessMaker.apiClient.interceptors.response.use((response) => { } }); +/** + * Next we will register the CSRF Token as a common header with Axios so that + * all outgoing HTTP requests automatically have it attached. This is just + * a simple convenience so we don't have to attach every token manually. + */ + +if (token) { + apiClient.defaults.headers.common["X-CSRF-TOKEN"] = token.content; +} else { + console.error("CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token"); +} + +if (window.Processmaker && window.Processmaker.apiTimeout !== undefined) { + apiTimeout = window.Processmaker.apiTimeout; +} + +apiClient.defaults.timeout = apiTimeout; + +setGlobalPMVariable("apiClient", apiClient); // Display any uncaught promise rejections from axios in the Process Maker alert box window.addEventListener("unhandledrejection", (event) => { @@ -131,4 +134,4 @@ window.addEventListener("unhandledrejection", (event) => { window.ProcessMaker.alert(error.message, "danger"); } } -}); \ No newline at end of file +}); diff --git a/resources/js/next/config/session.js b/resources/js/next/config/session.js index bdbba319f3..50aa68a264 100644 --- a/resources/js/next/config/session.js +++ b/resources/js/next/config/session.js @@ -1,17 +1,35 @@ -if (window.ProcessMaker.user) { +import { getGlobalPMVariable, setGlobalPMVariable, getGlobalVariable } from "../globalVariables"; + +const Vue = getGlobalVariable("Vue"); +const Echo = getGlobalVariable("Echo"); +const pushNotification = getGlobalVariable("pushNotification"); +const closeSessionModal = getGlobalVariable("closeSessionModal"); +const alert = getGlobalVariable("alert"); + +const timeoutScript = document.head.querySelector("meta[name=\"timeout-worker\"]")?.content; +const user = getGlobalPMVariable("user"); +const sessionModal = getGlobalPMVariable("sessionModal"); + +const isSameDevice = (e) => { + const localDeviceId = Vue.$cookies.get(e.device_variable); + const remoteDeviceId = e.device_id; + return localDeviceId && localDeviceId === remoteDeviceId; +}; + +if (user) { // Session timeout - const timeoutScript = document.head.querySelector("meta[name=\"timeout-worker\"]")?.content; - window.ProcessMaker.AccountTimeoutLength = parseInt(eval(document.head.querySelector("meta[name=\"timeout-length\"]")?.content)); - window.ProcessMaker.AccountTimeoutWarnSeconds = parseInt(document.head.querySelector("meta[name=\"timeout-warn-seconds\"]")?.content); - window.ProcessMaker.AccountTimeoutEnabled = document.head.querySelector("meta[name=\"timeout-enabled\"]") ? parseInt(document.head.querySelector("meta[name=\"timeout-enabled\"]")?.content) : 1; - window.ProcessMaker.AccountTimeoutWorker = new Worker(timeoutScript); - window.ProcessMaker.AccountTimeoutWorker.addEventListener("message", (e) => { + const AccountTimeoutLength = parseInt(eval(document.head.querySelector("meta[name=\"timeout-length\"]")?.content)); + const AccountTimeoutWarnSeconds = parseInt(document.head.querySelector("meta[name=\"timeout-warn-seconds\"]")?.content); + const AccountTimeoutEnabled = document.head.querySelector("meta[name=\"timeout-enabled\"]") ? parseInt(document.head.querySelector("meta[name=\"timeout-enabled\"]")?.content) : 1; + const AccountTimeoutWorker = new Worker(timeoutScript); + + AccountTimeoutWorker.addEventListener("message", (e) => { if (e.data.method === "countdown") { - window.ProcessMaker.sessionModal( + sessionModal( "Session Warning", "

Your user session is expiring. If your session expires, all of your unsaved data will be lost.

Would you like to stay connected?

", e.data.data.time, - window.ProcessMaker.AccountTimeoutWarnSeconds, + AccountTimeoutWarnSeconds, ); } if (e.data.method === "timedOut") { @@ -20,38 +38,37 @@ if (window.ProcessMaker.user) { }); // in some cases it's necessary to start manually - window.ProcessMaker.AccountTimeoutWorker.postMessage({ + AccountTimeoutWorker.postMessage({ method: "start", data: { - timeout: window.ProcessMaker.AccountTimeoutLength, - warnSeconds: window.ProcessMaker.AccountTimeoutWarnSeconds, - enabled: window.ProcessMaker.AccountTimeoutEnabled, + timeout: AccountTimeoutLength, + warnSeconds: AccountTimeoutWarnSeconds, + enabled: AccountTimeoutEnabled, }, }); - const isSameDevice = (e) => { - const localDeviceId = Vue.$cookies.get(e.device_variable); - const remoteDeviceId = e.device_id; - return localDeviceId && localDeviceId === remoteDeviceId; - }; + setGlobalPMVariable("AccountTimeoutWarnSeconds", AccountTimeoutLength); + setGlobalPMVariable("AccountTimeoutWarnSeconds", AccountTimeoutWarnSeconds); + setGlobalPMVariable("AccountTimeoutEnabled", AccountTimeoutEnabled); + setGlobalPMVariable("AccountTimeoutWorker", AccountTimeoutWorker); - window.Echo.private(`ProcessMaker.Models.User.${window.ProcessMaker.user.id}`) + Echo.private(`ProcessMaker.Models.User.${user.id}`) .notification((token) => { - ProcessMaker.pushNotification(token); + pushNotification(token); }) .listen(".SessionStarted", (e) => { const lifetime = parseInt(eval(e.lifetime)); if (isSameDevice(e)) { - window.ProcessMaker.AccountTimeoutWorker.postMessage({ + AccountTimeoutWorker.postMessage({ method: "start", data: { timeout: lifetime, - warnSeconds: window.ProcessMaker.AccountTimeoutWarnSeconds, - enabled: window.ProcessMaker.AccountTimeoutEnabled, + warnSeconds: AccountTimeoutWarnSeconds, + enabled: AccountTimeoutEnabled, }, }); - if (window.ProcessMaker.closeSessionModal) { - window.ProcessMaker.closeSessionModal(); + if (closeSessionModal) { + closeSessionModal(); } } }) @@ -71,9 +88,9 @@ if (window.ProcessMaker.user) { if (e.success) { const { link } = e; const { message } = e; - window.ProcessMaker.alert(message, "success", 0, false, false, link); + alert(message, "success", 0, false, false, link); } else { - window.ProcessMaker.alert(e.message, "warning"); + alert(e.message, "warning"); } }); } diff --git a/resources/js/next/config/user.js b/resources/js/next/config/user.js index f21a9b8b9e..c2f3a3ca7f 100644 --- a/resources/js/next/config/user.js +++ b/resources/js/next/config/user.js @@ -1,3 +1,5 @@ +import { setGlobalPMVariable } from "../globalVariables"; + import datetime_format from "../../data/datetime_formats.json"; const userID = document.head.querySelector("meta[name=\"user-id\"]"); @@ -7,14 +9,12 @@ const formatDate = document.head.querySelector("meta[name=\"datetime-format\"]") const timezone = document.head.querySelector("meta[name=\"timezone\"]"); const appUrl = document.head.querySelector("meta[name=\"app-url\"]"); -if (appUrl) { - window.ProcessMaker.app = { - url: appUrl.content, - }; -} +setGlobalPMVariable("app", appUrl ? { + url: appUrl.content, +} : null); if (userID) { - window.ProcessMaker.user = { + const user = { id: userID.content, datetime_format: formatDate?.content, calendar_format: formatDate?.content, @@ -22,10 +22,13 @@ if (userID) { fullName: userFullName?.content, avatar: userAvatar?.content, }; + datetime_format.forEach((value) => { if (formatDate.content === value.format) { - window.ProcessMaker.user.datetime_format = value.momentFormat; - window.ProcessMaker.user.calendar_format = value.calendarFormat; + user.datetime_format = value.momentFormat; + user.calendar_format = value.calendarFormat; } }); + + setGlobalPMVariable("user", user); } diff --git a/resources/js/next/globalVariables.js b/resources/js/next/globalVariables.js new file mode 100644 index 0000000000..42a0130581 --- /dev/null +++ b/resources/js/next/globalVariables.js @@ -0,0 +1,25 @@ +export default {}; + +export const setGlobalVariable = (key, value) => { + window[key] = value; +}; + +export const getGlobalVariable = (key) => window[key]; + +export const setGlobalPMVariable = (key, value) => { + if (!window.ProcessMaker) { + window.ProcessMaker = {}; + } + + window.ProcessMaker[key] = value; +}; + +export const setGlobalPMVariables = (variables) => { + if (!window.ProcessMaker) { + window.ProcessMaker = {}; + } + + Object.assign(window.ProcessMaker, variables); +}; + +export const getGlobalPMVariable = (key) => window.ProcessMaker[key]; diff --git a/resources/js/next/layout/navbar.js b/resources/js/next/layout/navbar.js index 2d23571bef..4fd47d661c 100644 --- a/resources/js/next/layout/navbar.js +++ b/resources/js/next/layout/navbar.js @@ -1,13 +1,20 @@ import newRequestModal from "../../components/requests/requestModal.vue"; import requestModal from "../../components/requests/modal.vue"; +import requestModalMobile from "../../components/requests/modalMobile.vue"; +import WelcomeModal from "../../Mobile/WelcomeModal.vue"; import notifications from "../../notifications/components/notifications.vue"; -import sessionModal from "../../components/Session.vue"; +import sessionModalComponent from "../../components/Session.vue"; import ConfirmationModal from "../../components/Confirm.vue"; import MessageModal from "../../components/Message.vue"; import NavbarProfile from "../../components/NavbarProfile.vue"; import Menu from "../../components/Menu.vue"; +import { getGlobalVariable, getGlobalPMVariable, setGlobalPMVariable } from "../globalVariables"; -window.Vue.component("LanguageSelectorButton", (resolve) => { +const Vue = getGlobalVariable("Vue"); +const $ = getGlobalVariable("$"); +const events = getGlobalPMVariable("events"); + +Vue.component("LanguageSelectorButton", (resolve) => { if (window.ProcessMaker.languageSelectorButtonComponent) { resolve(window.ProcessMaker.languageSelectorButtonComponent); } else { @@ -15,27 +22,16 @@ window.Vue.component("LanguageSelectorButton", (resolve) => { } }); -// Verify if is mobile +// Variables const browser = navigator.userAgent; const isMobileDevice = /Android|webOS|iPhone|iPad|iPod|BlackBerry|Windows Phone/i.test(browser); -window.ProcessMaker.mobileApp = false; -if (isMobileDevice) { - window.ProcessMaker.mobileApp = true; -} - -// Verify is in mobile mode -const isMobileNavbar = window.ProcessMaker.events.$cookies.get("isMobile"); -// Default alert functionality -window.ProcessMaker.alert = function (text, variant) { - if (typeof text === "string") { - window.alert(text); - } -}; +const mobileApp = !!isMobileDevice; +const isMobileNavbar = events.$cookies.get("isMobile"); // Verify is in mobile mode // Set our own specific alert function at the ProcessMaker global object that could // potentially be overwritten by some custom theme support -window.ProcessMaker.alert = function (msg, variant, showValue = 5, stayNextScreen = false, showLoader = false, msgLink = "", msgTitle = "") { +const alert = function (msg, variant, showValue = 5, stayNextScreen = false, showLoader = false, msgLink = "", msgTitle = "") { if (showValue === 0) { // Just show it indefinitely, no countdown showValue = true; @@ -56,14 +52,56 @@ window.ProcessMaker.alert = function (msg, variant, showValue = 5, stayNextScree }); }; +// Setup our login modal +const sessionModal = function (title, message, time, warnSeconds) { + ProcessMaker.navbar.sessionTitle = title || __("Session Warning"); + ProcessMaker.navbar.sessionMessage = message || __("Your session is about to expire."); + ProcessMaker.navbar.sessionTime = time; + ProcessMaker.navbar.sessionWarnSeconds = warnSeconds; + ProcessMaker.navbar.sessionShow = true; +}; + +const closeSessionModal = function () { + ProcessMaker.navbar.sessionShow = false; +}; + +// Set out own specific confirm modal. +const confirmModal = function ( + title, + message, + variant, + callback, + size = "md", + dataTestClose = "confirm-btn-close", + dataTestOk = "confirm-btn-ok", +) { + ProcessMaker.navbar.confirmTitle = title || __("Confirm"); + ProcessMaker.navbar.confirmMessage = message || __("Are you sure you want to delete?"); + ProcessMaker.navbar.confirmVariant = variant; + ProcessMaker.navbar.confirmCallback = callback; + ProcessMaker.navbar.confirmShow = true; + ProcessMaker.navbar.confirmSize = size; + ProcessMaker.navbar.confirmDataTestClose = dataTestClose; + ProcessMaker.navbar.confirmDataTestOk = dataTestOk; +}; + +// Set out own specific message modal. +const messageModal = function (title, message, variant, callback) { + ProcessMaker.navbar.messageTitle = title || __("Message"); + ProcessMaker.navbar.messageMessage = message || __(""); + ProcessMaker.navbar.messageVariant = variant; + ProcessMaker.navbar.messageCallback = callback; + ProcessMaker.navbar.messageShow = true; +}; + // Assign our navbar component to our global ProcessMaker object -window.ProcessMaker.navbar = new Vue({ +const navbar = new Vue({ el: "#navbar", components: { TopMenu: Menu, requestModal, notifications, - sessionModal, + sessionModal: sessionModalComponent, ConfirmationModal, MessageModal, NavbarProfile, @@ -122,7 +160,7 @@ window.ProcessMaker.navbar = new Vue({ ); } const findSB = setInterval(() => { - this.screenBuilder = window.ProcessMaker.ScreenBuilder; + this.screenBuilder = window.ProcessMaker.ScreenBuilder; // window.ProcessMaker.ScreenBuilder is not defined in the global scope if (this.screenBuilder) { clearInterval(findSB); } @@ -178,9 +216,19 @@ window.ProcessMaker.navbar = new Vue({ }, }); +setGlobalPMVariable("mobileApp", mobileApp); +setGlobalPMVariable("alert", alert); +setGlobalPMVariable("sessionModal", sessionModal); +setGlobalPMVariable("closeSessionModal", closeSessionModal); +setGlobalPMVariable("confirmModal", confirmModal); +setGlobalPMVariable("messageModal", messageModal); +setGlobalPMVariable("navbar", navbar); +// Breadcrumbs are now part of the navbar component. Alias it here. +setGlobalPMVariable("breadcrumbs", navbar); + // Assign our navbar component to our global ProcessMaker object if (isMobileNavbar === "true") { - window.ProcessMaker.navbarMobile = new Vue({ + const navbarMobile = new Vue({ el: "#navbarMobile", components: { requestModalMobile, @@ -206,7 +254,6 @@ if (isMobileNavbar === "true") { }, }, }); -} -// Breadcrumbs are now part of the navbar component. Alias it here. -window.ProcessMaker.breadcrumbs = window.ProcessMaker.navbar; + setGlobalPMVariable("navbarMobile", navbarMobile); +} diff --git a/resources/js/next/layout/sidebar.js b/resources/js/next/layout/sidebar.js index 7d496a373e..9dfeed2d87 100644 --- a/resources/js/next/layout/sidebar.js +++ b/resources/js/next/layout/sidebar.js @@ -1,8 +1,9 @@ import { sanitizeUrl } from "@braintree/sanitize-url"; // import VueHtml2Canvas from "vue-html2canvas"; import Sidebaricon from "../../components/Sidebaricon.vue"; +import { getGlobalVariable } from "../globalVariables"; -const { Vue } = window; +const Vue = getGlobalVariable("Vue"); // Vue.use(VueHtml2Canvas); Vue.prototype.$sanitize = sanitizeUrl; diff --git a/resources/js/next/libraries/bootstrap.js b/resources/js/next/libraries/bootstrap.js index 44bd5e25ee..c649b9221f 100644 --- a/resources/js/next/libraries/bootstrap.js +++ b/resources/js/next/libraries/bootstrap.js @@ -1,4 +1,6 @@ +import * as bootstrap from "bootstrap"; +import { setGlobalVariable } from "../globalVariables"; + import("bootstrap-vue/dist/bootstrap-vue.css"); -import("bootstrap").then((bootstrap) => { - window.bootstrap = bootstrap; -}); + +setGlobalVariable("bootstrap", bootstrap); diff --git a/resources/js/next/libraries/broadcast.js b/resources/js/next/libraries/broadcast.js index 17dabd94a7..c8a9c176d6 100644 --- a/resources/js/next/libraries/broadcast.js +++ b/resources/js/next/libraries/broadcast.js @@ -1,12 +1,16 @@ import Echo from "laravel-echo"; +import { setGlobalVariable, setGlobalPMVariable } from "../globalVariables"; +// Verify if the broadcasting is enabled if (window.Processmaker && window.Processmaker.broadcasting) { const config = window.Processmaker.broadcasting; - if (config.broadcaster == "pusher") { - window.Pusher = require("pusher-js"); - window.Pusher.logToConsole = config.debug; + if (config.broadcaster === "pusher") { + const Pusher = require("pusher-js"); + Pusher.logToConsole = config.debug; + + setGlobalVariable("Pusher", Pusher); } - window.Echo = new Echo(config); + setGlobalVariable("Echo", new Echo(config)); } diff --git a/resources/js/next/libraries/jquery.js b/resources/js/next/libraries/jquery.js index 9c0a4f29e7..40bc81e825 100644 --- a/resources/js/next/libraries/jquery.js +++ b/resources/js/next/libraries/jquery.js @@ -1 +1,6 @@ -window.$ = window.jQuery = require("jquery"); \ No newline at end of file +import * as jQuery from "jquery"; + +import { setGlobalVariable } from "../globalVariables"; + +setGlobalVariable("jQuery", jQuery); +setGlobalVariable("$", jQuery); diff --git a/resources/js/next/libraries/vueCookies.js b/resources/js/next/libraries/vueCookies.js index 4280af811f..a2d69e9509 100644 --- a/resources/js/next/libraries/vueCookies.js +++ b/resources/js/next/libraries/vueCookies.js @@ -1,3 +1,6 @@ import VueCookies from "vue-cookies"; +import { getGlobalVariable } from "../globalVariables"; -window.Vue.use(VueCookies); +const Vue = getGlobalVariable("Vue"); + +Vue.use(VueCookies); diff --git a/resources/js/next/libraries/vueFormElements.js b/resources/js/next/libraries/vueFormElements.js index 5276a0d411..c02226cc5e 100644 --- a/resources/js/next/libraries/vueFormElements.js +++ b/resources/js/next/libraries/vueFormElements.js @@ -1,3 +1,5 @@ +import { setGlobalVariable } from "../globalVariables"; + import("@processmaker/vue-form-elements").then((vueforms) => { - window.VueFormElements = vueforms; + setGlobalVariable("VueFormElements", vueforms); }); diff --git a/resources/js/next/libraries/vueRouter.js b/resources/js/next/libraries/vueRouter.js index eac8c1ef74..9e7b8e0da6 100644 --- a/resources/js/next/libraries/vueRouter.js +++ b/resources/js/next/libraries/vueRouter.js @@ -1,11 +1,12 @@ import Router from "vue-router"; -window.VueRouter = Router; +import { setGlobalVariable, getGlobalVariable, setGlobalPMVariable } from "../globalVariables"; -// if (!document.head.querySelector("meta[name=\"is-horizon\"]")) { -window.Vue.use(Router); -// } +const Vue = getGlobalVariable("Vue"); -window.ProcessMaker.Router = new Router({ +setGlobalVariable("VueRouter", Router); +setGlobalPMVariable("Router", new Router({ mode: "history", -}); +})); + +Vue.use(Router); diff --git a/resources/js/next/libraries/vuex.js b/resources/js/next/libraries/vuex.js index d0450f0e4c..07d2942bc8 100644 --- a/resources/js/next/libraries/vuex.js +++ b/resources/js/next/libraries/vuex.js @@ -1,5 +1,8 @@ import Vuex from "vuex"; import GlobalStore from "../../globalStore"; +import { getGlobalVariable } from "../globalVariables"; -window.Vue.use(Vuex); -window.Vue.use(GlobalStore); +const Vue = getGlobalVariable("Vue"); + +Vue.use(Vuex); +Vue.use(GlobalStore); diff --git a/resources/js/next/monaco.js b/resources/js/next/monaco.js index 2d07e7170f..f41018b6a0 100644 --- a/resources/js/next/monaco.js +++ b/resources/js/next/monaco.js @@ -1,5 +1,9 @@ import MonacoEditor from "vue-monaco"; -window.VueMonaco = require("vue-monaco"); +import { getGlobalVariable, setGlobalVariable } from "../globalVariables"; -window.Vue.component("monaco-editor", MonacoEditor); +const Vue = getGlobalVariable("Vue"); + +Vue.component("MonacoEditor", MonacoEditor); + +setGlobalVariable("VueMonaco", MonacoEditor); diff --git a/resources/js/next/screenBuilder.js b/resources/js/next/screenBuilder.js index d94bbacab1..f483b3ca10 100644 --- a/resources/js/next/screenBuilder.js +++ b/resources/js/next/screenBuilder.js @@ -1,7 +1,14 @@ -const addScriptsToDOM = async function(scripts) { +import { + getGlobalVariable, setGlobalVariable, getGlobalPMVariable, setGlobalPMVariable, +} from "../globalVariables"; + +const Vue = getGlobalVariable("Vue"); +const apiClient = getGlobalPMVariable("apiClient"); + +const addScriptsToDOM = async function (scripts) { for (const script of scripts) { await new Promise((resolve, reject) => { - const scriptElement = document.createElement('script'); + const scriptElement = document.createElement("script"); scriptElement.src = script; scriptElement.async = false; scriptElement.onload = resolve; @@ -11,38 +18,30 @@ const addScriptsToDOM = async function(scripts) { } }; -window.Vue.component('VueFormRenderer', function (resolve, reject) { - console.log("VUE FORM RENDERER") +Vue.component("VueFormRenderer", (resolve, reject) => { import("@processmaker/screen-builder/dist/vue-form-builder.css"); + if (screenBuilderScripts) { + addScriptsToDOM(screenBuilderScripts).then(() => { + import("@processmaker/screen-builder").then((ScreenBuilder) => { + Vue.use(ScreenBuilder.default); - if(screenBuilderScripts){ - console.log("SCREEN BUILDER SCRIPTS 7777", screenBuilderScripts) - addScriptsToDOM(screenBuilderScripts).then(()=>{ - import("@processmaker/screen-builder").then((ScreenBuilder)=>{ - window.Vue.use(ScreenBuilder.default); - window.ScreenBuilder = ScreenBuilder; - const { initializeScreenCache } = ScreenBuilder; // Configuration Global object used by ScreenBuilder // @link https://processmaker.atlassian.net/browse/FOUR-6833 Cache configuration const screenCacheEnabled = document.head.querySelector("meta[name=\"screen-cache-enabled\"]")?.content ?? "false"; const screenCacheTimeout = document.head.querySelector("meta[name=\"screen-cache-timeout\"]")?.content ?? "5000"; - window.ProcessMaker.screen = { + const screen = { cacheEnabled: screenCacheEnabled === "true", cacheTimeout: Number(screenCacheTimeout), }; // Initialize screen-builder cache - initializeScreenCache(window.ProcessMaker.apiClient, window.ProcessMaker.screen); - console.log("VUE FORM RESOLVE") + initializeScreenCache(apiClient, screen); + + setGlobalVariable("ScreenBuilder", ScreenBuilder); + setGlobalPMVariable("screen", screen); + resolve(ScreenBuilder.VueFormRenderer); }).catch(reject); - }) + }); } }); - - - - - - - diff --git a/resources/jscomposition/cases/casesMain/loader.js b/resources/jscomposition/cases/casesMain/loader.js index e3dab5f006..6762e9a72c 100644 --- a/resources/jscomposition/cases/casesMain/loader.js +++ b/resources/jscomposition/cases/casesMain/loader.js @@ -34,7 +34,6 @@ import("../../../js/next/config/user"); import("../../../js/next/config/session"); import("../../../js/next/config/momentConfig"); import("../../../js/next/config/notifications"); -import("../../../js/next/config/modals"); import("../../../js/next/config/openAI"); // Screen builder From 7cf6f2b2a29dd6ca22313b62ff35333183438820 Mon Sep 17 00:00:00 2001 From: Henry Jonas Date: Tue, 10 Dec 2024 12:20:24 -0400 Subject: [PATCH 07/30] FOUR-20583-A: Tasks Main change to new layout --- resources/js/next/config/notifications.js | 1 + resources/js/next/layout/navbar.js | 2 +- .../js/next/libraries/sharedComponents.js | 5 ++ resources/js/next/screenBuilder.js | 53 +++++++++------- resources/js/tasks/loaderMain.js | 59 +++++++++++++++++ resources/js/tasks/loaderShow.js | 63 +++++++++++++++++++ resources/js/tasks/show.js | 5 +- .../jscomposition/cases/casesDetail/loader.js | 50 +++++++++++++++ .../jscomposition/cases/casesMain/loader.js | 2 +- resources/views/cases/edit.blade.php | 32 ++++++---- resources/views/layouts/layoutnext.blade.php | 37 +++++------ resources/views/tasks/index.blade.php | 32 +++++++--- resources/views/tasks/preview.blade.php | 7 +-- webpack.mix.js | 3 + 14 files changed, 274 insertions(+), 77 deletions(-) create mode 100644 resources/js/next/libraries/sharedComponents.js create mode 100644 resources/js/tasks/loaderMain.js create mode 100644 resources/js/tasks/loaderShow.js create mode 100644 resources/jscomposition/cases/casesDetail/loader.js diff --git a/resources/js/next/config/notifications.js b/resources/js/next/config/notifications.js index 7887d1ce00..5f19349395 100644 --- a/resources/js/next/config/notifications.js +++ b/resources/js/next/config/notifications.js @@ -25,6 +25,7 @@ const removeNotifications = (messageIds = [], urls = []) => apiClient.put("/read const unreadNotifications = (messageIds = [], urls = []) => apiClient.put("/unread_notifications", { message_ids: messageIds, routes: urls }); +console.log("$notifications"); const $notifications = { icons: {}, }; diff --git a/resources/js/next/layout/navbar.js b/resources/js/next/layout/navbar.js index 4fd47d661c..21edd620a7 100644 --- a/resources/js/next/layout/navbar.js +++ b/resources/js/next/layout/navbar.js @@ -139,7 +139,7 @@ const navbar = new Vue({ sessionWarnSeconds: "", taskTitle: "", isMobile: false, - isMobileDevice: window.ProcessMaker.mobileApp, + isMobileDevice: mobileApp, }; }, watch: { diff --git a/resources/js/next/libraries/sharedComponents.js b/resources/js/next/libraries/sharedComponents.js new file mode 100644 index 0000000000..4d58b8b49b --- /dev/null +++ b/resources/js/next/libraries/sharedComponents.js @@ -0,0 +1,5 @@ +import { setGlobalVariable } from "../globalVariables"; + +import("../../components/shared").then((SharedComponents) => { + setGlobalVariable("SharedComponents", SharedComponents); +}); diff --git a/resources/js/next/screenBuilder.js b/resources/js/next/screenBuilder.js index f483b3ca10..780f8a46c4 100644 --- a/resources/js/next/screenBuilder.js +++ b/resources/js/next/screenBuilder.js @@ -1,6 +1,6 @@ import { getGlobalVariable, setGlobalVariable, getGlobalPMVariable, setGlobalPMVariable, -} from "../globalVariables"; +} from "./globalVariables"; const Vue = getGlobalVariable("Vue"); const apiClient = getGlobalPMVariable("apiClient"); @@ -18,30 +18,35 @@ const addScriptsToDOM = async function (scripts) { } }; -Vue.component("VueFormRenderer", (resolve, reject) => { - import("@processmaker/screen-builder/dist/vue-form-builder.css"); - if (screenBuilderScripts) { - addScriptsToDOM(screenBuilderScripts).then(() => { - import("@processmaker/screen-builder").then((ScreenBuilder) => { - Vue.use(ScreenBuilder.default); +const componentsScreenBuilder = ["VueFormRenderer", "Task"]; - const { initializeScreenCache } = ScreenBuilder; - // Configuration Global object used by ScreenBuilder - // @link https://processmaker.atlassian.net/browse/FOUR-6833 Cache configuration - const screenCacheEnabled = document.head.querySelector("meta[name=\"screen-cache-enabled\"]")?.content ?? "false"; - const screenCacheTimeout = document.head.querySelector("meta[name=\"screen-cache-timeout\"]")?.content ?? "5000"; - const screen = { - cacheEnabled: screenCacheEnabled === "true", - cacheTimeout: Number(screenCacheTimeout), - }; - // Initialize screen-builder cache - initializeScreenCache(apiClient, screen); +componentsScreenBuilder.forEach((component) => { + Vue.component(component, (resolve, reject) => { + import("@processmaker/screen-builder/dist/vue-form-builder.css"); + if (screenBuilderScripts) { + addScriptsToDOM(screenBuilderScripts).then(() => { + import("@processmaker/screen-builder").then((ScreenBuilder) => { + console.log("ScreenBuilder DESPUES", ScreenBuilder); + Vue.use(ScreenBuilder.default); - setGlobalVariable("ScreenBuilder", ScreenBuilder); - setGlobalPMVariable("screen", screen); + const { initializeScreenCache } = ScreenBuilder; + // Configuration Global object used by ScreenBuilder + // @link https://processmaker.atlassian.net/browse/FOUR-6833 Cache configuration + const screenCacheEnabled = document.head.querySelector("meta[name=\"screen-cache-enabled\"]")?.content ?? "false"; + const screenCacheTimeout = document.head.querySelector("meta[name=\"screen-cache-timeout\"]")?.content ?? "5000"; + const screen = { + cacheEnabled: screenCacheEnabled === "true", + cacheTimeout: Number(screenCacheTimeout), + }; - resolve(ScreenBuilder.VueFormRenderer); - }).catch(reject); - }); - } + setGlobalVariable("ScreenBuilder", ScreenBuilder); + setGlobalPMVariable("screen", screen); + // Initialize screen-builder cache + initializeScreenCache(apiClient, screen);// TODO: Its a bad practice to use the apiClient here + + resolve(ScreenBuilder[component]); + }).catch(reject); + }); + } + }); }); diff --git a/resources/js/tasks/loaderMain.js b/resources/js/tasks/loaderMain.js new file mode 100644 index 0000000000..6025a1cf0c --- /dev/null +++ b/resources/js/tasks/loaderMain.js @@ -0,0 +1,59 @@ +import Vue from "vue"; +import * as vue from "vue"; +import { BootstrapVue, BootstrapVueIcons } from "bootstrap-vue"; +import moment from "moment-timezone"; + +import { Multiselect } from "@processmaker/vue-multiselect"; +import AvatarImage from "../components/AvatarImage.vue"; +import TimelineItem from "../components/TimelineItem.vue"; +import SelectUserGroup from "../components/SelectUserGroup.vue"; +import PmqlInput from "../components/shared/PmqlInput.vue"; +import FilterTable from "../components/shared/FilterTable.vue"; +import PaginationTable from "../components/shared/PaginationTable.vue"; +import Required from "../components/shared/Required.vue"; + +console.log("loaderMain"); +window.Vue = Vue; +window.vue = vue; +window.moment = moment; + +window.Vue.prototype.moment = moment; +window.Vue.use(BootstrapVue); +window.Vue.use(BootstrapVueIcons); + +import("../next/libraries/vuex"); +import("../next/libraries/broadcast"); +import("../next/libraries/bootstrap"); +import("../next/libraries/jquery"); +import("../next/libraries/vueRouter"); +import("../next/libraries/vueCookies"); + +window.ProcessMaker = { + EventBus: new Vue(), + events: new Vue(), +}; + +import("../next/config/processmaker"); +import("../next/config/notifications"); +import("../next/config/i18n"); +import("../next/config/user"); +import("../next/config/session"); +import("../next/config/momentConfig"); +import("../next/config/openAI"); + +// Load components +import("../next/libraries/vueFormElements"); +import("../next/libraries/sharedComponents"); + +// Load libraries dependencies first +import("../next/layout/sidebar"); +import("../next/layout/navbar"); + +Vue.component("AvatarImage", AvatarImage); +Vue.component("TimelineItem", TimelineItem); +Vue.component("SelectUserGroup", SelectUserGroup); +Vue.component("PmqlInput", PmqlInput); +Vue.component("FilterTable", FilterTable); +Vue.component("PaginationTable", PaginationTable); +Vue.component("Multiselect", Multiselect); +Vue.component("Required", Required); diff --git a/resources/js/tasks/loaderShow.js b/resources/js/tasks/loaderShow.js new file mode 100644 index 0000000000..0a6e7dfd32 --- /dev/null +++ b/resources/js/tasks/loaderShow.js @@ -0,0 +1,63 @@ +import Vue from "vue"; +import * as vue from "vue"; +import { BootstrapVue, BootstrapVueIcons } from "bootstrap-vue"; +import moment from "moment-timezone"; + +import { Multiselect } from "@processmaker/vue-multiselect"; +import AvatarImage from "../components/AvatarImage.vue"; +import TimelineItem from "../components/TimelineItem.vue"; +import SelectUserGroup from "../components/SelectUserGroup.vue"; +import PmqlInput from "../components/shared/PmqlInput.vue"; +import FilterTable from "../components/shared/FilterTable.vue"; +import PaginationTable from "../components/shared/PaginationTable.vue"; +import PMDropdownSuggest from "../components/PMDropdownSuggest.vue"; +import Required from "../components/shared/Required.vue"; + +window.Vue = Vue; +window.vue = vue; +window.moment = moment; + +window.Vue.prototype.moment = moment; +window.Vue.use(BootstrapVue); +window.Vue.use(BootstrapVueIcons); + +import("../next/libraries/vuex"); +import("../next/libraries/broadcast"); +import("../next/libraries/bootstrap"); +import("../next/libraries/jquery"); +import("../next/libraries/vueRouter"); +import("../next/libraries/vueCookies"); + +window.ProcessMaker = { + EventBus: new Vue(), + events: new Vue(), +}; + +import("../next/config/processmaker"); +import("../next/config/notifications"); +import("../next/config/i18n"); +import("../next/config/user"); +import("../next/config/session"); +import("../next/config/momentConfig"); +import("../next/config/openAI"); + +// Load components +import("../next/libraries/vueFormElements"); +import("../next/libraries/sharedComponents"); + +// Screen builder +import("../next/screenBuilder"); + +// Load libraries dependencies first +import("../next/layout/sidebar"); +import("../next/layout/navbar"); + +Vue.component("AvatarImage", AvatarImage); +Vue.component("TimelineItem", TimelineItem); +Vue.component("SelectUserGroup", SelectUserGroup); +Vue.component("PmqlInput", PmqlInput); +Vue.component("FilterTable", FilterTable); +Vue.component("PaginationTable", PaginationTable); +Vue.component("PMDropdownSuggest", PMDropdownSuggest); +Vue.component("Required", Required); +Vue.component("Multiselect", Multiselect); diff --git a/resources/js/tasks/show.js b/resources/js/tasks/show.js index 3f937034e9..3fc704a2b0 100644 --- a/resources/js/tasks/show.js +++ b/resources/js/tasks/show.js @@ -1,8 +1,8 @@ import Vue from "vue"; import Vuex from "vuex"; -import Task from "@processmaker/screen-builder"; import MonacoEditor from "vue-monaco"; import debounce from "lodash/debounce"; +import Mustache from "mustache"; import TaskView from "./components/TaskView.vue"; import NavbarTaskMobile from "./components/NavbarTaskMobile.vue"; import AvatarImage from "../components/AvatarImage.vue"; @@ -13,12 +13,10 @@ import TasksList from "./components/TasksList.vue"; import TaskSavePanel from "./components/TaskSavePanel.vue"; import autosaveMixins from "../modules/autosave/autosaveMixin"; import draftFileUploadMixin from "../modules/autosave/draftFileUploadMixin"; -import Mustache from "mustache"; import TaskSaveNotification from "./components/TaskSaveNotification.vue"; import reassignMixin from "../common/reassignMixin"; Vue.use(Vuex); -Vue.use("task", Task); Vue.component("TaskView", TaskView); Vue.component("NavbarTaskMobile", NavbarTaskMobile); Vue.component("AvatarImage", AvatarImage); @@ -29,7 +27,6 @@ Vue.component("QuickFillPreview", QuickFillPreview); Vue.component("TasksList", TasksList); Vue.component("TaskSavePanel", TaskSavePanel); Vue.component("TaskSaveNotification", TaskSaveNotification); -Vue.component("PMDropdownSuggest", PMDropdownSuggest); Vue.mixin(autosaveMixins); Vue.mixin(draftFileUploadMixin); diff --git a/resources/jscomposition/cases/casesDetail/loader.js b/resources/jscomposition/cases/casesDetail/loader.js new file mode 100644 index 0000000000..6bc7628c7d --- /dev/null +++ b/resources/jscomposition/cases/casesDetail/loader.js @@ -0,0 +1,50 @@ +import Vue from "vue"; +import * as vue from "vue"; +import { BootstrapVue, BootstrapVueIcons } from "bootstrap-vue"; +import moment from "moment-timezone"; + +import AvatarImage from "../../../js/components/AvatarImage.vue"; +import TimelineItem from "../../../js/components/TimelineItem.vue"; +import SelectUserGroup from "../../../js/components/SelectUserGroup.vue"; + +window.Vue = Vue; +window.vue = vue; +window.moment = moment; + +window.Vue.prototype.moment = moment; +window.Vue.use(BootstrapVue); +window.Vue.use(BootstrapVueIcons); + +import("../../../js/next/libraries/vuex"); +import("../../../js/next/libraries/broadcast"); +import("../../../js/next/libraries/bootstrap"); +import("../../../js/next/libraries/jquery"); +import("../../../js/next/libraries/vueRouter"); +import("../../../js/next/libraries/vueCookies"); +// Load components +import("../../../js/next/libraries/vueFormElements"); +import("../../../js/next/libraries/sharedComponents"); + +window.ProcessMaker = { + EventBus: new Vue(), + events: new Vue(), +}; + +import("../../../js/next/config/processmaker"); +import("../../../js/next/config/notifications"); +import("../../../js/next/config/i18n"); +import("../../../js/next/config/user"); +import("../../../js/next/config/session"); +import("../../../js/next/config/momentConfig"); +import("../../../js/next/config/openAI"); + +// Screen builder +import("../../../js/next/screenBuilder"); + +// Load libraries dependencies first +import("../../../js/next/layout/sidebar"); +import("../../../js/next/layout/navbar"); + +Vue.component("AvatarImage", AvatarImage); +Vue.component("TimelineItem", TimelineItem); +Vue.component("SelectUserGroup", SelectUserGroup); diff --git a/resources/jscomposition/cases/casesMain/loader.js b/resources/jscomposition/cases/casesMain/loader.js index 6762e9a72c..e319628064 100644 --- a/resources/jscomposition/cases/casesMain/loader.js +++ b/resources/jscomposition/cases/casesMain/loader.js @@ -29,11 +29,11 @@ window.ProcessMaker = { }; import("../../../js/next/config/processmaker"); +import("../../../js/next/config/notifications"); import("../../../js/next/config/i18n"); import("../../../js/next/config/user"); import("../../../js/next/config/session"); import("../../../js/next/config/momentConfig"); -import("../../../js/next/config/notifications"); import("../../../js/next/config/openAI"); // Screen builder diff --git a/resources/views/cases/edit.blade.php b/resources/views/cases/edit.blade.php index 9832c8ea9d..0de68bd135 100644 --- a/resources/views/cases/edit.blade.php +++ b/resources/views/cases/edit.blade.php @@ -1,4 +1,4 @@ -@extends('layouts.layout',['content_margin' => '', 'overflow-auto' => '']) +@extends('layouts.layoutnext',['content_margin' => '', 'overflow-auto' => '']) @section('title') {{ __('Case Detail') }} @@ -51,21 +51,21 @@ class="tw-flex tw-justify-center tw-items-center tw-gap-2 data-test="cancel-case-button" > - {{ __('Cancel Case') }} + @{{ $t('Cancel Case') }} @endif
- @{{ __(statusLabel) }} + @{{ $t(statusLabel) }}
  • -

    @{{ __(labelDate) }}:

    +

    @{{ $t(labelDate) }}:

    @{{ moment(statusDate).format() }}
  • @if ($request->user_id)
  • -

    {{ __('STARTED BY') }}:

    +

    @{{ $t('STARTED BY') }}:

    - {{ __('Web Entry') }} + @{{ $t('Web Entry') }}
  • @endif
  • -

    {{ __('LAUNCHPAD') }}

    +

    @{{ $t('LAUNCHPAD') }}

  • @if ($request->participants->count())
  • -

    {{ __('PARTICIPANTS') }}:

    +

    @{{ $t('PARTICIPANTS') }}:

    -