4 changes: 3 additions & 1 deletion ProcessMaker/Http/Controllers/CasesController.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -20,10 +20,12 @@ class CasesController extends Controller
*/
public function index()
{
$manager = app(ScreenBuilderManager::class);
event(new ScreenBuilderStarting($manager, 'FORM'));
$currentUser = Auth::user()->only(['id', 'username', 'fullname', 'firstname', 'lastname', 'avatar']);

// This is a temporary API the engine team will provide the new
return view('cases.casesMain', compact('currentUser'));
return view('cases.casesMain', compact('currentUser', 'manager'));
}

/**
Expand Down
2 changes: 1 addition & 1 deletion resources/js/components/TreeView.vue
Original file line numberDiff line numberDiff line change
Expand Up@@ -37,7 +37,7 @@ export default {
},
},
mounted() {
const jsonCrackEmbed = this.$refs.jsonCrackEmbed;
const { jsonCrackEmbed } = this.$refs;
const json = this.jsonData === "" ? "{}" : this.jsonData;
const options = {
theme: "light",
Expand Down
53 changes: 53 additions & 0 deletions resources/js/next/badPractices.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
/**
* BAD PRACTICES
*/

// Use multiple forms of an event bus.
window.ProcessMaker = {
EventBus: new Vue(),
events: new Vue()
};

// Force to overload the vue object
window.Vue.prototype.moment = moment;

// Load all statements in a single file bootstrap.js

// Overload the window.ProcessMaker variable without using the setGlobalVariable function

// Use local variables instead of window.ProcessMaker
window.ProcessMaker.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);
}
});
});
};

// Remove the saved search unneeded
class InjectJavascript
{
public function handle($request, Closure $next)
{
$response = $next($request);
if (get_class($response) !== Response::class) {
return $response;
}
$content = $response->getContent();
$tag = '';
$tag .= '<script src="' . mix('/js/addSaveButton.js', 'vendor/processmaker/packages/package-savedsearch') . '"></script>';
$tag .= '<script src="' . mix('/js/listenForRecounts.js', 'vendor/processmaker/packages/package-savedsearch') . '"></script>';
$content = str_replace('</body>', $tag . "\n</body>", $content);
$response->setContent($content);

return $response;
}
}

112 changes: 112 additions & 0 deletions resources/js/next/components/index.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
import { getGlobalVariable } from "../globalVariables";

// Multiselect

const Vue = getGlobalVariable("Vue");

const pmComponents = {
// Components folder
AvatarImage: () => import("../../components/AvatarImage.vue"),
Breadcrumbs: () => import("../../components/Breadcrumbs.vue"),
Confirm: () => import("../../components/Confirm.vue"),
CustomActions: () => import("../../components/CustomActions.vue"),
DetailRow: () => import("../../components/DetailRow.vue"),
FilterBar: () => import("../../components/FilterBar.vue"),
Menu: () => import("../../components/Menu.vue"),
Message: () => import("../../components/Message.vue"),
NavbarProfile: () => import("../../components/NavbarProfile.vue"),
PMBadgesFilters: () => import("../../components/PMBadgesFilters.vue"),
PMDatetimePicker: () => import("../../components/PMDatetimePicker.vue"),
PMDropdownSuggest: () => import("../../components/PMDropdownSuggest.vue"),
PMFloatingButtons: () => import("../../components/PMFloatingButtons.vue"),
PMFormSelectSuggest: () => import("../../components/PMFormSelectSuggest.vue"),
PMMessageResults: () => import("../../components/PMMessageResults.vue"),
PMMessageScreen: () => import("../../components/PMMessageScreen.vue"),
PMPanelWithCustomHeader: () => import("../../components/PMPanelWithCustomHeader.vue"),
PMPopoverConfirmation: () => import("../../components/PMPopoverConfirmation.vue"),
PMSearchBar: () => import("../../components/PMSearchBar.vue"),
PMTable: () => import("../../components/PMTable.vue"),
PMTabs: () => import("../../components/PMTabs.vue"),
Recommendations: () => import("../../components/Recommendations.vue"),
SelectFromApi: () => import("../../components/SelectFromApi.vue"),
SelectLanguage: () => import("../../components/SelectLanguage.vue"),
SelectScreen: () => import("../../components/SelectScreen.vue"),
SelectStatus: () => import("../../components/SelectStatus.vue"),
SelectUser: () => import("../../components/SelectUser.vue"),
SelectUserGroup: () => import("../../components/SelectUserGroup.vue"),
Session: () => import("../../components/Session.vue"),
Sidebaricon: () => import("../../components/Sidebaricon.vue"),
Timeline: () => import("../../components/Timeline.vue"),
TimelineItem: () => import("../../components/TimelineItem.vue"),
TreeView: () => import("../../components/TreeView.vue"),
// Shared components folder
AddToBundle: () => import("../../components/shared/AddToBundle"),
AddToProjectModal: () => import("../../components/shared/AddToProjectModal"),
AssetDependentTreeModal: () => import("../../components/shared/AssetDependentTreeModal.vue"),
AssetTreeModal: () => import("../../components/shared/AssetTreeModal.vue"),
BackendSelect: () => import("../../components/shared/BackendSelect.vue"),
BasicSearch: () => import("../../components/shared/BasicSearch.vue"),
CategorySelect: () => import("../../components/shared/CategorySelect.vue"),
ChangeLog: () => import("../../components/shared/ChangeLog.vue"),
ColorSchemeSelector: () => import("../../components/shared/ColorSchemeSelector.vue"),
Column: () => import("../../components/shared/Column.vue"),
ColumnChooser: () => import("../../components/shared/ColumnChooser.vue"),
ColumnConfig: () => import("../../components/shared/ColumnConfig.vue"),
DataCard: () => import("../../components/shared/DataCard.vue"),
DataFormatSelector: () => import("../../components/shared/DataFormatSelector.vue"),
DataMaskSelector: () => import("../../components/shared/DataMaskSelector.vue"),
DataNode: () => import("../../components/shared/DataNode.vue"),
DataTree: () => import("../../components/shared/DataTree.vue"),
DownloadSvgButton: () => import("../../components/shared/DownloadSvgButton.vue"),
DraggableFileUpload: () => import("../../components/shared/DraggableFileUpload.vue"),
EllipsisMenu: () => import("../../components/shared/EllipsisMenu.vue"),
FileUploadButton: () => import("../../components/shared/FileUploadButton.vue"),
FilterTable: () => import("../../components/shared/FilterTable.vue"),
IconDropdown: () => import("../../components/shared/IconDropdown.vue"),
IconSelector: () => import("../../components/shared/IconSelector.vue"),
InputImageCarousel: () => import("../../components/shared/InputImageCarousel.vue"),
LaunchpadSettingsModal: () => import("../../components/shared/LaunchpadSettingsModal.vue"),
Modal: () => import("../../components/shared/Modal.vue"),
ModalSaveVersion: () => import("../../components/shared/ModalSaveVersion.vue"),
MultiThumbnailFileUploader: () => import("../../components/shared/MultiThumbnailFileUploader.vue"),
PaginationTable: () => import("../../components/shared/PaginationTable.vue"),
PmqlInput: () => import("../../components/shared/PmqlInput.vue"),
PmqlInputFilters: () => import("../../components/shared/PmqlInputFilters.vue"),
ProjectSelect: () => import("../../components/shared/ProjectSelect.vue"),
PTab: () => import("../../components/shared/PTab.vue"),
PTabs: () => import("../../components/shared/PTabs.vue"),
Required: () => import("../../components/shared/Required.vue"),
SidebarButton: () => import("../../components/shared/SidebarButton.vue"),
SidebarNav: () => import("../../components/shared/SidebarNav.vue"),
SliderWithInput: () => import("../../components/shared/SliderWithInput.vue"),
// Common components folder
DataTreeToggle: () => import("../../components/common/data-tree-toggle.vue"),
// Tasks components folder
MobileTasks: () => import("../../tasks/components/MobileTasks.vue"),
NavbarTaskMobile: () => import("../../tasks/components/NavbarTaskMobile.vue"),
QuickFillPreview: () => import("../../tasks/components/QuickFillPreview.vue"),
ReassignMobileModal: () => import("../../tasks/components/ReassignMobileModal.vue"),
SplitpaneContainer: () => import("../../tasks/components/SplitpaneContainer.vue"),
TaskDetailsMobile: () => import("../../tasks/components/TaskDetailsMobile.vue"),
TaskListRowButtons: () => import("../../tasks/components/TaskListRowButtons.vue"),
TaskLoading: () => import("../../tasks/components/TaskLoading.vue"),
TaskSaveNotification: () => import("../../tasks/components/TaskSaveNotification.vue"),
TaskSavePanel: () => import("../../tasks/components/TaskSavePanel.vue"),
TasksHome: () => import("../../tasks/components/TasksHome.vue"),
TasksList: () => import("../../tasks/components/TasksList.vue"),
TasksListCounter: () => import("../../tasks/components/TasksListCounter.vue"),
TasksPreview: () => import("../../tasks/components/TasksPreview.vue"),
TaskTooltip: () => import("../../tasks/components/TaskTooltip.vue"),
TaskView: () => import("../../tasks/components/TaskView.vue"),
};

Object.entries(pmComponents).forEach(([key, component]) => {
Vue.component(key, component);
});

// Multiselect
Vue.component("Multiselect", (resolve, reject) => {
import("@processmaker/vue-multiselect").then((Multiselect) => {
resolve(Multiselect.Multiselect);
}).catch(reject);
});
6 changes: 6 additions & 0 deletions resources/js/next/config/accesibility.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
import AccessibilityMixin from "./components/common/mixins/accessibility";
import { getGlobalVariable } from "../globalVariables";

const Vue = getGlobalVariable("Vue");

Vue.mixin(AccessibilityMixin);
59 changes: 59 additions & 0 deletions resources/js/next/config/i18n.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
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";
import { setGlobalPMVariable, getGlobalVariable } from "../globalVariables";

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,
);

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
nsSeparator: false,
keySeparator: false,
parseMissingKeyHandler(value) {
if (!translationsLoaded) { return value; }
// Report that a translation is missing
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" },
],
},
});

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);
36 changes: 36 additions & 0 deletions resources/js/next/config/notifications.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
import { getGlobalPMVariable, setGlobalPMVariable } from "../globalVariables";

const apiClient = getGlobalPMVariable("apiClient");

const notifications = [];

const pushNotification = (notification) => {
if (notifications.filter((x) => x.id === notification).length === 0) {
notifications.push(notification);
}
};

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);
});

urls.forEach((url) => {
const messageIndex = notifications.findIndex((x) => x.url === url);
if (messageIndex >= 0) {
removeNotifications(notifications[messageIndex].id);
}
});
});

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);
9 changes: 9 additions & 0 deletions resources/js/next/config/openAI.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
import { setGlobalPMVariable } from "../globalVariables";

const openAi = document.head.querySelector("meta[name=\"open-ai-nlq-to-pmql\"]");

setGlobalPMVariable("openAi", openAi ? {
enabled: openAi.content,
} : {
enabled: false,
});
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
4 changes: 3 additions & 1 deletion ProcessMaker/Http/Controllers/CasesController.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -20,10 +20,12 @@ class CasesController extends Controller
*/
public function index()
{
$manager = app(ScreenBuilderManager::class);
event(new ScreenBuilderStarting($manager, 'FORM'));
$currentUser = Auth::user()->only(['id', 'username', 'fullname', 'firstname', 'lastname', 'avatar']);

// This is a temporary API the engine team will provide the new
return view('cases.casesMain', compact('currentUser'));
return view('cases.casesMain', compact('currentUser', 'manager'));
}

/**
Expand Down
2 changes: 1 addition & 1 deletion resources/js/components/TreeView.vue
Original file line numberDiff line numberDiff line change
Expand Up@@ -37,7 +37,7 @@ export default {
},
},
mounted() {
const jsonCrackEmbed = this.$refs.jsonCrackEmbed;
const { jsonCrackEmbed } = this.$refs;
const json = this.jsonData === "" ? "{}" : this.jsonData;
const options = {
theme: "light",
Expand Down
53 changes: 53 additions & 0 deletions resources/js/next/badPractices.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
/**
* BAD PRACTICES
*/

// Use multiple forms of an event bus.
window.ProcessMaker = {
EventBus: new Vue(),
events: new Vue()
};

// Force to overload the vue object
window.Vue.prototype.moment = moment;

// Load all statements in a single file bootstrap.js

// Overload the window.ProcessMaker variable without using the setGlobalVariable function

// Use local variables instead of window.ProcessMaker
window.ProcessMaker.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);
}
});
});
};

// Remove the saved search unneeded
class InjectJavascript
{
public function handle($request, Closure $next)
{
$response = $next($request);
if (get_class($response) !== Response::class) {
return $response;
}
$content = $response->getContent();
$tag = '';
$tag .= '<script src="' . mix('/js/addSaveButton.js', 'vendor/processmaker/packages/package-savedsearch') . '"></script>';
$tag .= '<script src="' . mix('/js/listenForRecounts.js', 'vendor/processmaker/packages/package-savedsearch') . '"></script>';
$content = str_replace('</body>', $tag . "\n</body>", $content);
$response->setContent($content);

return $response;
}
}

112 changes: 112 additions & 0 deletions resources/js/next/components/index.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
import { getGlobalVariable } from "../globalVariables";

// Multiselect

const Vue = getGlobalVariable("Vue");

const pmComponents = {
// Components folder
AvatarImage: () => import("../../components/AvatarImage.vue"),
Breadcrumbs: () => import("../../components/Breadcrumbs.vue"),
Confirm: () => import("../../components/Confirm.vue"),
CustomActions: () => import("../../components/CustomActions.vue"),
DetailRow: () => import("../../components/DetailRow.vue"),
FilterBar: () => import("../../components/FilterBar.vue"),
Menu: () => import("../../components/Menu.vue"),
Message: () => import("../../components/Message.vue"),
NavbarProfile: () => import("../../components/NavbarProfile.vue"),
PMBadgesFilters: () => import("../../components/PMBadgesFilters.vue"),
PMDatetimePicker: () => import("../../components/PMDatetimePicker.vue"),
PMDropdownSuggest: () => import("../../components/PMDropdownSuggest.vue"),
PMFloatingButtons: () => import("../../components/PMFloatingButtons.vue"),
PMFormSelectSuggest: () => import("../../components/PMFormSelectSuggest.vue"),
PMMessageResults: () => import("../../components/PMMessageResults.vue"),
PMMessageScreen: () => import("../../components/PMMessageScreen.vue"),
PMPanelWithCustomHeader: () => import("../../components/PMPanelWithCustomHeader.vue"),
PMPopoverConfirmation: () => import("../../components/PMPopoverConfirmation.vue"),
PMSearchBar: () => import("../../components/PMSearchBar.vue"),
PMTable: () => import("../../components/PMTable.vue"),
PMTabs: () => import("../../components/PMTabs.vue"),
Recommendations: () => import("../../components/Recommendations.vue"),
SelectFromApi: () => import("../../components/SelectFromApi.vue"),
SelectLanguage: () => import("../../components/SelectLanguage.vue"),
SelectScreen: () => import("../../components/SelectScreen.vue"),
SelectStatus: () => import("../../components/SelectStatus.vue"),
SelectUser: () => import("../../components/SelectUser.vue"),
SelectUserGroup: () => import("../../components/SelectUserGroup.vue"),
Session: () => import("../../components/Session.vue"),
Sidebaricon: () => import("../../components/Sidebaricon.vue"),
Timeline: () => import("../../components/Timeline.vue"),
TimelineItem: () => import("../../components/TimelineItem.vue"),
TreeView: () => import("../../components/TreeView.vue"),
// Shared components folder
AddToBundle: () => import("../../components/shared/AddToBundle"),
AddToProjectModal: () => import("../../components/shared/AddToProjectModal"),
AssetDependentTreeModal: () => import("../../components/shared/AssetDependentTreeModal.vue"),
AssetTreeModal: () => import("../../components/shared/AssetTreeModal.vue"),
BackendSelect: () => import("../../components/shared/BackendSelect.vue"),
BasicSearch: () => import("../../components/shared/BasicSearch.vue"),
CategorySelect: () => import("../../components/shared/CategorySelect.vue"),
ChangeLog: () => import("../../components/shared/ChangeLog.vue"),
ColorSchemeSelector: () => import("../../components/shared/ColorSchemeSelector.vue"),
Column: () => import("../../components/shared/Column.vue"),
ColumnChooser: () => import("../../components/shared/ColumnChooser.vue"),
ColumnConfig: () => import("../../components/shared/ColumnConfig.vue"),
DataCard: () => import("../../components/shared/DataCard.vue"),
DataFormatSelector: () => import("../../components/shared/DataFormatSelector.vue"),
DataMaskSelector: () => import("../../components/shared/DataMaskSelector.vue"),
DataNode: () => import("../../components/shared/DataNode.vue"),
DataTree: () => import("../../components/shared/DataTree.vue"),
DownloadSvgButton: () => import("../../components/shared/DownloadSvgButton.vue"),
DraggableFileUpload: () => import("../../components/shared/DraggableFileUpload.vue"),
EllipsisMenu: () => import("../../components/shared/EllipsisMenu.vue"),
FileUploadButton: () => import("../../components/shared/FileUploadButton.vue"),
FilterTable: () => import("../../components/shared/FilterTable.vue"),
IconDropdown: () => import("../../components/shared/IconDropdown.vue"),
IconSelector: () => import("../../components/shared/IconSelector.vue"),
InputImageCarousel: () => import("../../components/shared/InputImageCarousel.vue"),
LaunchpadSettingsModal: () => import("../../components/shared/LaunchpadSettingsModal.vue"),
Modal: () => import("../../components/shared/Modal.vue"),
ModalSaveVersion: () => import("../../components/shared/ModalSaveVersion.vue"),
MultiThumbnailFileUploader: () => import("../../components/shared/MultiThumbnailFileUploader.vue"),
PaginationTable: () => import("../../components/shared/PaginationTable.vue"),
PmqlInput: () => import("../../components/shared/PmqlInput.vue"),
PmqlInputFilters: () => import("../../components/shared/PmqlInputFilters.vue"),
ProjectSelect: () => import("../../components/shared/ProjectSelect.vue"),
PTab: () => import("../../components/shared/PTab.vue"),
PTabs: () => import("../../components/shared/PTabs.vue"),
Required: () => import("../../components/shared/Required.vue"),
SidebarButton: () => import("../../components/shared/SidebarButton.vue"),
SidebarNav: () => import("../../components/shared/SidebarNav.vue"),
SliderWithInput: () => import("../../components/shared/SliderWithInput.vue"),
// Common components folder
DataTreeToggle: () => import("../../components/common/data-tree-toggle.vue"),
// Tasks components folder
MobileTasks: () => import("../../tasks/components/MobileTasks.vue"),
NavbarTaskMobile: () => import("../../tasks/components/NavbarTaskMobile.vue"),
QuickFillPreview: () => import("../../tasks/components/QuickFillPreview.vue"),
ReassignMobileModal: () => import("../../tasks/components/ReassignMobileModal.vue"),
SplitpaneContainer: () => import("../../tasks/components/SplitpaneContainer.vue"),
TaskDetailsMobile: () => import("../../tasks/components/TaskDetailsMobile.vue"),
TaskListRowButtons: () => import("../../tasks/components/TaskListRowButtons.vue"),
TaskLoading: () => import("../../tasks/components/TaskLoading.vue"),
TaskSaveNotification: () => import("../../tasks/components/TaskSaveNotification.vue"),
TaskSavePanel: () => import("../../tasks/components/TaskSavePanel.vue"),
TasksHome: () => import("../../tasks/components/TasksHome.vue"),
TasksList: () => import("../../tasks/components/TasksList.vue"),
TasksListCounter: () => import("../../tasks/components/TasksListCounter.vue"),
TasksPreview: () => import("../../tasks/components/TasksPreview.vue"),
TaskTooltip: () => import("../../tasks/components/TaskTooltip.vue"),
TaskView: () => import("../../tasks/components/TaskView.vue"),
};

Object.entries(pmComponents).forEach(([key, component]) => {
Vue.component(key, component);
});

// Multiselect
Vue.component("Multiselect", (resolve, reject) => {
import("@processmaker/vue-multiselect").then((Multiselect) => {
resolve(Multiselect.Multiselect);
}).catch(reject);
});
6 changes: 6 additions & 0 deletions resources/js/next/config/accesibility.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
import AccessibilityMixin from "./components/common/mixins/accessibility";
import { getGlobalVariable } from "../globalVariables";

const Vue = getGlobalVariable("Vue");

Vue.mixin(AccessibilityMixin);
59 changes: 59 additions & 0 deletions resources/js/next/config/i18n.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
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";
import { setGlobalPMVariable, getGlobalVariable } from "../globalVariables";

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,
);

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
nsSeparator: false,
keySeparator: false,
parseMissingKeyHandler(value) {
if (!translationsLoaded) { return value; }
// Report that a translation is missing
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" },
],
},
});

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);
36 changes: 36 additions & 0 deletions resources/js/next/config/notifications.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
import { getGlobalPMVariable, setGlobalPMVariable } from "../globalVariables";

const apiClient = getGlobalPMVariable("apiClient");

const notifications = [];

const pushNotification = (notification) => {
if (notifications.filter((x) => x.id === notification).length === 0) {
notifications.push(notification);
}
};

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);
});

urls.forEach((url) => {
const messageIndex = notifications.findIndex((x) => x.url === url);
if (messageIndex >= 0) {
removeNotifications(notifications[messageIndex].id);
}
});
});

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);
9 changes: 9 additions & 0 deletions resources/js/next/config/openAI.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
import { setGlobalPMVariable } from "../globalVariables";

const openAi = document.head.querySelector("meta[name=\"open-ai-nlq-to-pmql\"]");

setGlobalPMVariable("openAi", openAi ? {
enabled: openAi.content,
} : {
enabled: false,
});
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
4 changes: 3 additions & 1 deletion ProcessMaker/Http/Controllers/CasesController.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -20,10 +20,12 @@ class CasesController extends Controller
*/
public function index()
{
$manager = app(ScreenBuilderManager::class);
event(new ScreenBuilderStarting($manager, 'FORM'));
$currentUser = Auth::user()->only(['id', 'username', 'fullname', 'firstname', 'lastname', 'avatar']);

// This is a temporary API the engine team will provide the new
return view('cases.casesMain', compact('currentUser'));
return view('cases.casesMain', compact('currentUser', 'manager'));
}

/**
Expand Down
2 changes: 1 addition & 1 deletion resources/js/components/TreeView.vue
Original file line numberDiff line numberDiff line change
Expand Up@@ -37,7 +37,7 @@ export default {
},
},
mounted() {
const jsonCrackEmbed = this.$refs.jsonCrackEmbed;
const { jsonCrackEmbed } = this.$refs;
const json = this.jsonData === "" ? "{}" : this.jsonData;
const options = {
theme: "light",
Expand Down
53 changes: 53 additions & 0 deletions resources/js/next/badPractices.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
/**
* BAD PRACTICES
*/

// Use multiple forms of an event bus.
window.ProcessMaker = {
EventBus: new Vue(),
events: new Vue()
};

// Force to overload the vue object
window.Vue.prototype.moment = moment;

// Load all statements in a single file bootstrap.js

// Overload the window.ProcessMaker variable without using the setGlobalVariable function

// Use local variables instead of window.ProcessMaker
window.ProcessMaker.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);
}
});
});
};

// Remove the saved search unneeded
class InjectJavascript
{
public function handle($request, Closure $next)
{
$response = $next($request);
if (get_class($response) !== Response::class) {
return $response;
}
$content = $response->getContent();
$tag = '';
$tag .= '<script src="' . mix('/js/addSaveButton.js', 'vendor/processmaker/packages/package-savedsearch') . '"></script>';
$tag .= '<script src="' . mix('/js/listenForRecounts.js', 'vendor/processmaker/packages/package-savedsearch') . '"></script>';
$content = str_replace('</body>', $tag . "\n</body>", $content);
$response->setContent($content);

return $response;
}
}

112 changes: 112 additions & 0 deletions resources/js/next/components/index.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
import { getGlobalVariable } from "../globalVariables";

// Multiselect

const Vue = getGlobalVariable("Vue");

const pmComponents = {
// Components folder
AvatarImage: () => import("../../components/AvatarImage.vue"),
Breadcrumbs: () => import("../../components/Breadcrumbs.vue"),
Confirm: () => import("../../components/Confirm.vue"),
CustomActions: () => import("../../components/CustomActions.vue"),
DetailRow: () => import("../../components/DetailRow.vue"),
FilterBar: () => import("../../components/FilterBar.vue"),
Menu: () => import("../../components/Menu.vue"),
Message: () => import("../../components/Message.vue"),
NavbarProfile: () => import("../../components/NavbarProfile.vue"),
PMBadgesFilters: () => import("../../components/PMBadgesFilters.vue"),
PMDatetimePicker: () => import("../../components/PMDatetimePicker.vue"),
PMDropdownSuggest: () => import("../../components/PMDropdownSuggest.vue"),
PMFloatingButtons: () => import("../../components/PMFloatingButtons.vue"),
PMFormSelectSuggest: () => import("../../components/PMFormSelectSuggest.vue"),
PMMessageResults: () => import("../../components/PMMessageResults.vue"),
PMMessageScreen: () => import("../../components/PMMessageScreen.vue"),
PMPanelWithCustomHeader: () => import("../../components/PMPanelWithCustomHeader.vue"),
PMPopoverConfirmation: () => import("../../components/PMPopoverConfirmation.vue"),
PMSearchBar: () => import("../../components/PMSearchBar.vue"),
PMTable: () => import("../../components/PMTable.vue"),
PMTabs: () => import("../../components/PMTabs.vue"),
Recommendations: () => import("../../components/Recommendations.vue"),
SelectFromApi: () => import("../../components/SelectFromApi.vue"),
SelectLanguage: () => import("../../components/SelectLanguage.vue"),
SelectScreen: () => import("../../components/SelectScreen.vue"),
SelectStatus: () => import("../../components/SelectStatus.vue"),
SelectUser: () => import("../../components/SelectUser.vue"),
SelectUserGroup: () => import("../../components/SelectUserGroup.vue"),
Session: () => import("../../components/Session.vue"),
Sidebaricon: () => import("../../components/Sidebaricon.vue"),
Timeline: () => import("../../components/Timeline.vue"),
TimelineItem: () => import("../../components/TimelineItem.vue"),
TreeView: () => import("../../components/TreeView.vue"),
// Shared components folder
AddToBundle: () => import("../../components/shared/AddToBundle"),
AddToProjectModal: () => import("../../components/shared/AddToProjectModal"),
AssetDependentTreeModal: () => import("../../components/shared/AssetDependentTreeModal.vue"),
AssetTreeModal: () => import("../../components/shared/AssetTreeModal.vue"),
BackendSelect: () => import("../../components/shared/BackendSelect.vue"),
BasicSearch: () => import("../../components/shared/BasicSearch.vue"),
CategorySelect: () => import("../../components/shared/CategorySelect.vue"),
ChangeLog: () => import("../../components/shared/ChangeLog.vue"),
ColorSchemeSelector: () => import("../../components/shared/ColorSchemeSelector.vue"),
Column: () => import("../../components/shared/Column.vue"),
ColumnChooser: () => import("../../components/shared/ColumnChooser.vue"),
ColumnConfig: () => import("../../components/shared/ColumnConfig.vue"),
DataCard: () => import("../../components/shared/DataCard.vue"),
DataFormatSelector: () => import("../../components/shared/DataFormatSelector.vue"),
DataMaskSelector: () => import("../../components/shared/DataMaskSelector.vue"),
DataNode: () => import("../../components/shared/DataNode.vue"),
DataTree: () => import("../../components/shared/DataTree.vue"),
DownloadSvgButton: () => import("../../components/shared/DownloadSvgButton.vue"),
DraggableFileUpload: () => import("../../components/shared/DraggableFileUpload.vue"),
EllipsisMenu: () => import("../../components/shared/EllipsisMenu.vue"),
FileUploadButton: () => import("../../components/shared/FileUploadButton.vue"),
FilterTable: () => import("../../components/shared/FilterTable.vue"),
IconDropdown: () => import("../../components/shared/IconDropdown.vue"),
IconSelector: () => import("../../components/shared/IconSelector.vue"),
InputImageCarousel: () => import("../../components/shared/InputImageCarousel.vue"),
LaunchpadSettingsModal: () => import("../../components/shared/LaunchpadSettingsModal.vue"),
Modal: () => import("../../components/shared/Modal.vue"),
ModalSaveVersion: () => import("../../components/shared/ModalSaveVersion.vue"),
MultiThumbnailFileUploader: () => import("../../components/shared/MultiThumbnailFileUploader.vue"),
PaginationTable: () => import("../../components/shared/PaginationTable.vue"),
PmqlInput: () => import("../../components/shared/PmqlInput.vue"),
PmqlInputFilters: () => import("../../components/shared/PmqlInputFilters.vue"),
ProjectSelect: () => import("../../components/shared/ProjectSelect.vue"),
PTab: () => import("../../components/shared/PTab.vue"),
PTabs: () => import("../../components/shared/PTabs.vue"),
Required: () => import("../../components/shared/Required.vue"),
SidebarButton: () => import("../../components/shared/SidebarButton.vue"),
SidebarNav: () => import("../../components/shared/SidebarNav.vue"),
SliderWithInput: () => import("../../components/shared/SliderWithInput.vue"),
// Common components folder
DataTreeToggle: () => import("../../components/common/data-tree-toggle.vue"),
// Tasks components folder
MobileTasks: () => import("../../tasks/components/MobileTasks.vue"),
NavbarTaskMobile: () => import("../../tasks/components/NavbarTaskMobile.vue"),
QuickFillPreview: () => import("../../tasks/components/QuickFillPreview.vue"),
ReassignMobileModal: () => import("../../tasks/components/ReassignMobileModal.vue"),
SplitpaneContainer: () => import("../../tasks/components/SplitpaneContainer.vue"),
TaskDetailsMobile: () => import("../../tasks/components/TaskDetailsMobile.vue"),
TaskListRowButtons: () => import("../../tasks/components/TaskListRowButtons.vue"),
TaskLoading: () => import("../../tasks/components/TaskLoading.vue"),
TaskSaveNotification: () => import("../../tasks/components/TaskSaveNotification.vue"),
TaskSavePanel: () => import("../../tasks/components/TaskSavePanel.vue"),
TasksHome: () => import("../../tasks/components/TasksHome.vue"),
TasksList: () => import("../../tasks/components/TasksList.vue"),
TasksListCounter: () => import("../../tasks/components/TasksListCounter.vue"),
TasksPreview: () => import("../../tasks/components/TasksPreview.vue"),
TaskTooltip: () => import("../../tasks/components/TaskTooltip.vue"),
TaskView: () => import("../../tasks/components/TaskView.vue"),
};

Object.entries(pmComponents).forEach(([key, component]) => {
Vue.component(key, component);
});

// Multiselect
Vue.component("Multiselect", (resolve, reject) => {
import("@processmaker/vue-multiselect").then((Multiselect) => {
resolve(Multiselect.Multiselect);
}).catch(reject);
});
6 changes: 6 additions & 0 deletions resources/js/next/config/accesibility.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
import AccessibilityMixin from "./components/common/mixins/accessibility";
import { getGlobalVariable } from "../globalVariables";

const Vue = getGlobalVariable("Vue");

Vue.mixin(AccessibilityMixin);
59 changes: 59 additions & 0 deletions resources/js/next/config/i18n.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
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";
import { setGlobalPMVariable, getGlobalVariable } from "../globalVariables";

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,
);

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
nsSeparator: false,
keySeparator: false,
parseMissingKeyHandler(value) {
if (!translationsLoaded) { return value; }
// Report that a translation is missing
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" },
],
},
});

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);
36 changes: 36 additions & 0 deletions resources/js/next/config/notifications.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
import { getGlobalPMVariable, setGlobalPMVariable } from "../globalVariables";

const apiClient = getGlobalPMVariable("apiClient");

const notifications = [];

const pushNotification = (notification) => {
if (notifications.filter((x) => x.id === notification).length === 0) {
notifications.push(notification);
}
};

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);
});

urls.forEach((url) => {
const messageIndex = notifications.findIndex((x) => x.url === url);
if (messageIndex >= 0) {
removeNotifications(notifications[messageIndex].id);
}
});
});

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);
9 changes: 9 additions & 0 deletions resources/js/next/config/openAI.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
import { setGlobalPMVariable } from "../globalVariables";

const openAi = document.head.querySelector("meta[name=\"open-ai-nlq-to-pmql\"]");

setGlobalPMVariable("openAi", openAi ? {
enabled: openAi.content,
} : {
enabled: false,
});
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
4 changes: 3 additions & 1 deletion ProcessMaker/Http/Controllers/CasesController.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -20,10 +20,12 @@ class CasesController extends Controller
*/
public function index()
{
$manager = app(ScreenBuilderManager::class);
event(new ScreenBuilderStarting($manager, 'FORM'));
$currentUser = Auth::user()->only(['id', 'username', 'fullname', 'firstname', 'lastname', 'avatar']);

// This is a temporary API the engine team will provide the new
return view('cases.casesMain', compact('currentUser'));
return view('cases.casesMain', compact('currentUser', 'manager'));
}

/**
Expand Down
2 changes: 1 addition & 1 deletion resources/js/components/TreeView.vue
Original file line numberDiff line numberDiff line change
Expand Up@@ -37,7 +37,7 @@ export default {
},
},
mounted() {
const jsonCrackEmbed = this.$refs.jsonCrackEmbed;
const { jsonCrackEmbed } = this.$refs;
const json = this.jsonData === "" ? "{}" : this.jsonData;
const options = {
theme: "light",
Expand Down
53 changes: 53 additions & 0 deletions resources/js/next/badPractices.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
/**
* BAD PRACTICES
*/

// Use multiple forms of an event bus.
window.ProcessMaker = {
EventBus: new Vue(),
events: new Vue()
};

// Force to overload the vue object
window.Vue.prototype.moment = moment;

// Load all statements in a single file bootstrap.js

// Overload the window.ProcessMaker variable without using the setGlobalVariable function

// Use local variables instead of window.ProcessMaker
window.ProcessMaker.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);
}
});
});
};

// Remove the saved search unneeded
class InjectJavascript
{
public function handle($request, Closure $next)
{
$response = $next($request);
if (get_class($response) !== Response::class) {
return $response;
}
$content = $response->getContent();
$tag = '';
$tag .= '<script src="' . mix('/js/addSaveButton.js', 'vendor/processmaker/packages/package-savedsearch') . '"></script>';
$tag .= '<script src="' . mix('/js/listenForRecounts.js', 'vendor/processmaker/packages/package-savedsearch') . '"></script>';
$content = str_replace('</body>', $tag . "\n</body>", $content);
$response->setContent($content);

return $response;
}
}

112 changes: 112 additions & 0 deletions resources/js/next/components/index.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
import { getGlobalVariable } from "../globalVariables";

// Multiselect

const Vue = getGlobalVariable("Vue");

const pmComponents = {
// Components folder
AvatarImage: () => import("../../components/AvatarImage.vue"),
Breadcrumbs: () => import("../../components/Breadcrumbs.vue"),
Confirm: () => import("../../components/Confirm.vue"),
CustomActions: () => import("../../components/CustomActions.vue"),
DetailRow: () => import("../../components/DetailRow.vue"),
FilterBar: () => import("../../components/FilterBar.vue"),
Menu: () => import("../../components/Menu.vue"),
Message: () => import("../../components/Message.vue"),
NavbarProfile: () => import("../../components/NavbarProfile.vue"),
PMBadgesFilters: () => import("../../components/PMBadgesFilters.vue"),
PMDatetimePicker: () => import("../../components/PMDatetimePicker.vue"),
PMDropdownSuggest: () => import("../../components/PMDropdownSuggest.vue"),
PMFloatingButtons: () => import("../../components/PMFloatingButtons.vue"),
PMFormSelectSuggest: () => import("../../components/PMFormSelectSuggest.vue"),
PMMessageResults: () => import("../../components/PMMessageResults.vue"),
PMMessageScreen: () => import("../../components/PMMessageScreen.vue"),
PMPanelWithCustomHeader: () => import("../../components/PMPanelWithCustomHeader.vue"),
PMPopoverConfirmation: () => import("../../components/PMPopoverConfirmation.vue"),
PMSearchBar: () => import("../../components/PMSearchBar.vue"),
PMTable: () => import("../../components/PMTable.vue"),
PMTabs: () => import("../../components/PMTabs.vue"),
Recommendations: () => import("../../components/Recommendations.vue"),
SelectFromApi: () => import("../../components/SelectFromApi.vue"),
SelectLanguage: () => import("../../components/SelectLanguage.vue"),
SelectScreen: () => import("../../components/SelectScreen.vue"),
SelectStatus: () => import("../../components/SelectStatus.vue"),
SelectUser: () => import("../../components/SelectUser.vue"),
SelectUserGroup: () => import("../../components/SelectUserGroup.vue"),
Session: () => import("../../components/Session.vue"),
Sidebaricon: () => import("../../components/Sidebaricon.vue"),
Timeline: () => import("../../components/Timeline.vue"),
TimelineItem: () => import("../../components/TimelineItem.vue"),
TreeView: () => import("../../components/TreeView.vue"),
// Shared components folder
AddToBundle: () => import("../../components/shared/AddToBundle"),
AddToProjectModal: () => import("../../components/shared/AddToProjectModal"),
AssetDependentTreeModal: () => import("../../components/shared/AssetDependentTreeModal.vue"),
AssetTreeModal: () => import("../../components/shared/AssetTreeModal.vue"),
BackendSelect: () => import("../../components/shared/BackendSelect.vue"),
BasicSearch: () => import("../../components/shared/BasicSearch.vue"),
CategorySelect: () => import("../../components/shared/CategorySelect.vue"),
ChangeLog: () => import("../../components/shared/ChangeLog.vue"),
ColorSchemeSelector: () => import("../../components/shared/ColorSchemeSelector.vue"),
Column: () => import("../../components/shared/Column.vue"),
ColumnChooser: () => import("../../components/shared/ColumnChooser.vue"),
ColumnConfig: () => import("../../components/shared/ColumnConfig.vue"),
DataCard: () => import("../../components/shared/DataCard.vue"),
DataFormatSelector: () => import("../../components/shared/DataFormatSelector.vue"),
DataMaskSelector: () => import("../../components/shared/DataMaskSelector.vue"),
DataNode: () => import("../../components/shared/DataNode.vue"),
DataTree: () => import("../../components/shared/DataTree.vue"),
DownloadSvgButton: () => import("../../components/shared/DownloadSvgButton.vue"),
DraggableFileUpload: () => import("../../components/shared/DraggableFileUpload.vue"),
EllipsisMenu: () => import("../../components/shared/EllipsisMenu.vue"),
FileUploadButton: () => import("../../components/shared/FileUploadButton.vue"),
FilterTable: () => import("../../components/shared/FilterTable.vue"),
IconDropdown: () => import("../../components/shared/IconDropdown.vue"),
IconSelector: () => import("../../components/shared/IconSelector.vue"),
InputImageCarousel: () => import("../../components/shared/InputImageCarousel.vue"),
LaunchpadSettingsModal: () => import("../../components/shared/LaunchpadSettingsModal.vue"),
Modal: () => import("../../components/shared/Modal.vue"),
ModalSaveVersion: () => import("../../components/shared/ModalSaveVersion.vue"),
MultiThumbnailFileUploader: () => import("../../components/shared/MultiThumbnailFileUploader.vue"),
PaginationTable: () => import("../../components/shared/PaginationTable.vue"),
PmqlInput: () => import("../../components/shared/PmqlInput.vue"),
PmqlInputFilters: () => import("../../components/shared/PmqlInputFilters.vue"),
ProjectSelect: () => import("../../components/shared/ProjectSelect.vue"),
PTab: () => import("../../components/shared/PTab.vue"),
PTabs: () => import("../../components/shared/PTabs.vue"),
Required: () => import("../../components/shared/Required.vue"),
SidebarButton: () => import("../../components/shared/SidebarButton.vue"),
SidebarNav: () => import("../../components/shared/SidebarNav.vue"),
SliderWithInput: () => import("../../components/shared/SliderWithInput.vue"),
// Common components folder
DataTreeToggle: () => import("../../components/common/data-tree-toggle.vue"),
// Tasks components folder
MobileTasks: () => import("../../tasks/components/MobileTasks.vue"),
NavbarTaskMobile: () => import("../../tasks/components/NavbarTaskMobile.vue"),
QuickFillPreview: () => import("../../tasks/components/QuickFillPreview.vue"),
ReassignMobileModal: () => import("../../tasks/components/ReassignMobileModal.vue"),
SplitpaneContainer: () => import("../../tasks/components/SplitpaneContainer.vue"),
TaskDetailsMobile: () => import("../../tasks/components/TaskDetailsMobile.vue"),
TaskListRowButtons: () => import("../../tasks/components/TaskListRowButtons.vue"),
TaskLoading: () => import("../../tasks/components/TaskLoading.vue"),
TaskSaveNotification: () => import("../../tasks/components/TaskSaveNotification.vue"),
TaskSavePanel: () => import("../../tasks/components/TaskSavePanel.vue"),
TasksHome: () => import("../../tasks/components/TasksHome.vue"),
TasksList: () => import("../../tasks/components/TasksList.vue"),
TasksListCounter: () => import("../../tasks/components/TasksListCounter.vue"),
TasksPreview: () => import("../../tasks/components/TasksPreview.vue"),
TaskTooltip: () => import("../../tasks/components/TaskTooltip.vue"),
TaskView: () => import("../../tasks/components/TaskView.vue"),
};

Object.entries(pmComponents).forEach(([key, component]) => {
Vue.component(key, component);
});

// Multiselect
Vue.component("Multiselect", (resolve, reject) => {
import("@processmaker/vue-multiselect").then((Multiselect) => {
resolve(Multiselect.Multiselect);
}).catch(reject);
});
6 changes: 6 additions & 0 deletions resources/js/next/config/accesibility.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
import AccessibilityMixin from "./components/common/mixins/accessibility";
import { getGlobalVariable } from "../globalVariables";

const Vue = getGlobalVariable("Vue");

Vue.mixin(AccessibilityMixin);
59 changes: 59 additions & 0 deletions resources/js/next/config/i18n.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
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";
import { setGlobalPMVariable, getGlobalVariable } from "../globalVariables";

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,
);

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
nsSeparator: false,
keySeparator: false,
parseMissingKeyHandler(value) {
if (!translationsLoaded) { return value; }
// Report that a translation is missing
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" },
],
},
});

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);
36 changes: 36 additions & 0 deletions resources/js/next/config/notifications.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
import { getGlobalPMVariable, setGlobalPMVariable } from "../globalVariables";

const apiClient = getGlobalPMVariable("apiClient");

const notifications = [];

const pushNotification = (notification) => {
if (notifications.filter((x) => x.id === notification).length === 0) {
notifications.push(notification);
}
};

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);
});

urls.forEach((url) => {
const messageIndex = notifications.findIndex((x) => x.url === url);
if (messageIndex >= 0) {
removeNotifications(notifications[messageIndex].id);
}
});
});

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);
9 changes: 9 additions & 0 deletions resources/js/next/config/openAI.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
import { setGlobalPMVariable } from "../globalVariables";

const openAi = document.head.querySelector("meta[name=\"open-ai-nlq-to-pmql\"]");

setGlobalPMVariable("openAi", openAi ? {
enabled: openAi.content,
} : {
enabled: false,
});
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
4 changes: 3 additions & 1 deletion ProcessMaker/Http/Controllers/CasesController.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -20,10 +20,12 @@ class CasesController extends Controller
*/
public function index()
{
$manager = app(ScreenBuilderManager::class);
event(new ScreenBuilderStarting($manager, 'FORM'));
$currentUser = Auth::user()->only(['id', 'username', 'fullname', 'firstname', 'lastname', 'avatar']);

// This is a temporary API the engine team will provide the new
return view('cases.casesMain', compact('currentUser'));
return view('cases.casesMain', compact('currentUser', 'manager'));
}

/**
Expand Down
2 changes: 1 addition & 1 deletion resources/js/components/TreeView.vue
Original file line numberDiff line numberDiff line change
Expand Up@@ -37,7 +37,7 @@ export default {
},
},
mounted() {
const jsonCrackEmbed = this.$refs.jsonCrackEmbed;
const { jsonCrackEmbed } = this.$refs;
const json = this.jsonData === "" ? "{}" : this.jsonData;
const options = {
theme: "light",
Expand Down
53 changes: 53 additions & 0 deletions resources/js/next/badPractices.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
/**
* BAD PRACTICES
*/

// Use multiple forms of an event bus.
window.ProcessMaker = {
EventBus: new Vue(),
events: new Vue()
};

// Force to overload the vue object
window.Vue.prototype.moment = moment;

// Load all statements in a single file bootstrap.js

// Overload the window.ProcessMaker variable without using the setGlobalVariable function

// Use local variables instead of window.ProcessMaker
window.ProcessMaker.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);
}
});
});
};

// Remove the saved search unneeded
class InjectJavascript
{
public function handle($request, Closure $next)
{
$response = $next($request);
if (get_class($response) !== Response::class) {
return $response;
}
$content = $response->getContent();
$tag = '';
$tag .= '<script src="' . mix('/js/addSaveButton.js', 'vendor/processmaker/packages/package-savedsearch') . '"></script>';
$tag .= '<script src="' . mix('/js/listenForRecounts.js', 'vendor/processmaker/packages/package-savedsearch') . '"></script>';
$content = str_replace('</body>', $tag . "\n</body>", $content);
$response->setContent($content);

return $response;
}
}

112 changes: 112 additions & 0 deletions resources/js/next/components/index.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
import { getGlobalVariable } from "../globalVariables";

// Multiselect

const Vue = getGlobalVariable("Vue");

const pmComponents = {
// Components folder
AvatarImage: () => import("../../components/AvatarImage.vue"),
Breadcrumbs: () => import("../../components/Breadcrumbs.vue"),
Confirm: () => import("../../components/Confirm.vue"),
CustomActions: () => import("../../components/CustomActions.vue"),
DetailRow: () => import("../../components/DetailRow.vue"),
FilterBar: () => import("../../components/FilterBar.vue"),
Menu: () => import("../../components/Menu.vue"),
Message: () => import("../../components/Message.vue"),
NavbarProfile: () => import("../../components/NavbarProfile.vue"),
PMBadgesFilters: () => import("../../components/PMBadgesFilters.vue"),
PMDatetimePicker: () => import("../../components/PMDatetimePicker.vue"),
PMDropdownSuggest: () => import("../../components/PMDropdownSuggest.vue"),
PMFloatingButtons: () => import("../../components/PMFloatingButtons.vue"),
PMFormSelectSuggest: () => import("../../components/PMFormSelectSuggest.vue"),
PMMessageResults: () => import("../../components/PMMessageResults.vue"),
PMMessageScreen: () => import("../../components/PMMessageScreen.vue"),
PMPanelWithCustomHeader: () => import("../../components/PMPanelWithCustomHeader.vue"),
PMPopoverConfirmation: () => import("../../components/PMPopoverConfirmation.vue"),
PMSearchBar: () => import("../../components/PMSearchBar.vue"),
PMTable: () => import("../../components/PMTable.vue"),
PMTabs: () => import("../../components/PMTabs.vue"),
Recommendations: () => import("../../components/Recommendations.vue"),
SelectFromApi: () => import("../../components/SelectFromApi.vue"),
SelectLanguage: () => import("../../components/SelectLanguage.vue"),
SelectScreen: () => import("../../components/SelectScreen.vue"),
SelectStatus: () => import("../../components/SelectStatus.vue"),
SelectUser: () => import("../../components/SelectUser.vue"),
SelectUserGroup: () => import("../../components/SelectUserGroup.vue"),
Session: () => import("../../components/Session.vue"),
Sidebaricon: () => import("../../components/Sidebaricon.vue"),
Timeline: () => import("../../components/Timeline.vue"),
TimelineItem: () => import("../../components/TimelineItem.vue"),
TreeView: () => import("../../components/TreeView.vue"),
// Shared components folder
AddToBundle: () => import("../../components/shared/AddToBundle"),
AddToProjectModal: () => import("../../components/shared/AddToProjectModal"),
AssetDependentTreeModal: () => import("../../components/shared/AssetDependentTreeModal.vue"),
AssetTreeModal: () => import("../../components/shared/AssetTreeModal.vue"),
BackendSelect: () => import("../../components/shared/BackendSelect.vue"),
BasicSearch: () => import("../../components/shared/BasicSearch.vue"),
CategorySelect: () => import("../../components/shared/CategorySelect.vue"),
ChangeLog: () => import("../../components/shared/ChangeLog.vue"),
ColorSchemeSelector: () => import("../../components/shared/ColorSchemeSelector.vue"),
Column: () => import("../../components/shared/Column.vue"),
ColumnChooser: () => import("../../components/shared/ColumnChooser.vue"),
ColumnConfig: () => import("../../components/shared/ColumnConfig.vue"),
DataCard: () => import("../../components/shared/DataCard.vue"),
DataFormatSelector: () => import("../../components/shared/DataFormatSelector.vue"),
DataMaskSelector: () => import("../../components/shared/DataMaskSelector.vue"),
DataNode: () => import("../../components/shared/DataNode.vue"),
DataTree: () => import("../../components/shared/DataTree.vue"),
DownloadSvgButton: () => import("../../components/shared/DownloadSvgButton.vue"),
DraggableFileUpload: () => import("../../components/shared/DraggableFileUpload.vue"),
EllipsisMenu: () => import("../../components/shared/EllipsisMenu.vue"),
FileUploadButton: () => import("../../components/shared/FileUploadButton.vue"),
FilterTable: () => import("../../components/shared/FilterTable.vue"),
IconDropdown: () => import("../../components/shared/IconDropdown.vue"),
IconSelector: () => import("../../components/shared/IconSelector.vue"),
InputImageCarousel: () => import("../../components/shared/InputImageCarousel.vue"),
LaunchpadSettingsModal: () => import("../../components/shared/LaunchpadSettingsModal.vue"),
Modal: () => import("../../components/shared/Modal.vue"),
ModalSaveVersion: () => import("../../components/shared/ModalSaveVersion.vue"),
MultiThumbnailFileUploader: () => import("../../components/shared/MultiThumbnailFileUploader.vue"),
PaginationTable: () => import("../../components/shared/PaginationTable.vue"),
PmqlInput: () => import("../../components/shared/PmqlInput.vue"),
PmqlInputFilters: () => import("../../components/shared/PmqlInputFilters.vue"),
ProjectSelect: () => import("../../components/shared/ProjectSelect.vue"),
PTab: () => import("../../components/shared/PTab.vue"),
PTabs: () => import("../../components/shared/PTabs.vue"),
Required: () => import("../../components/shared/Required.vue"),
SidebarButton: () => import("../../components/shared/SidebarButton.vue"),
SidebarNav: () => import("../../components/shared/SidebarNav.vue"),
SliderWithInput: () => import("../../components/shared/SliderWithInput.vue"),
// Common components folder
DataTreeToggle: () => import("../../components/common/data-tree-toggle.vue"),
// Tasks components folder
MobileTasks: () => import("../../tasks/components/MobileTasks.vue"),
NavbarTaskMobile: () => import("../../tasks/components/NavbarTaskMobile.vue"),
QuickFillPreview: () => import("../../tasks/components/QuickFillPreview.vue"),
ReassignMobileModal: () => import("../../tasks/components/ReassignMobileModal.vue"),
SplitpaneContainer: () => import("../../tasks/components/SplitpaneContainer.vue"),
TaskDetailsMobile: () => import("../../tasks/components/TaskDetailsMobile.vue"),
TaskListRowButtons: () => import("../../tasks/components/TaskListRowButtons.vue"),
TaskLoading: () => import("../../tasks/components/TaskLoading.vue"),
TaskSaveNotification: () => import("../../tasks/components/TaskSaveNotification.vue"),
TaskSavePanel: () => import("../../tasks/components/TaskSavePanel.vue"),
TasksHome: () => import("../../tasks/components/TasksHome.vue"),
TasksList: () => import("../../tasks/components/TasksList.vue"),
TasksListCounter: () => import("../../tasks/components/TasksListCounter.vue"),
TasksPreview: () => import("../../tasks/components/TasksPreview.vue"),
TaskTooltip: () => import("../../tasks/components/TaskTooltip.vue"),
TaskView: () => import("../../tasks/components/TaskView.vue"),
};

Object.entries(pmComponents).forEach(([key, component]) => {
Vue.component(key, component);
});

// Multiselect
Vue.component("Multiselect", (resolve, reject) => {
import("@processmaker/vue-multiselect").then((Multiselect) => {
resolve(Multiselect.Multiselect);
}).catch(reject);
});
6 changes: 6 additions & 0 deletions resources/js/next/config/accesibility.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
import AccessibilityMixin from "./components/common/mixins/accessibility";
import { getGlobalVariable } from "../globalVariables";

const Vue = getGlobalVariable("Vue");

Vue.mixin(AccessibilityMixin);
59 changes: 59 additions & 0 deletions resources/js/next/config/i18n.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
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";
import { setGlobalPMVariable, getGlobalVariable } from "../globalVariables";

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,
);

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
nsSeparator: false,
keySeparator: false,
parseMissingKeyHandler(value) {
if (!translationsLoaded) { return value; }
// Report that a translation is missing
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" },
],
},
});

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);
36 changes: 36 additions & 0 deletions resources/js/next/config/notifications.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
import { getGlobalPMVariable, setGlobalPMVariable } from "../globalVariables";

const apiClient = getGlobalPMVariable("apiClient");

const notifications = [];

const pushNotification = (notification) => {
if (notifications.filter((x) => x.id === notification).length === 0) {
notifications.push(notification);
}
};

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);
});

urls.forEach((url) => {
const messageIndex = notifications.findIndex((x) => x.url === url);
if (messageIndex >= 0) {
removeNotifications(notifications[messageIndex].id);
}
});
});

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);
9 changes: 9 additions & 0 deletions resources/js/next/config/openAI.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
import { setGlobalPMVariable } from "../globalVariables";

const openAi = document.head.querySelector("meta[name=\"open-ai-nlq-to-pmql\"]");

setGlobalPMVariable("openAi", openAi ? {
enabled: openAi.content,
} : {
enabled: false,
});
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
4 changes: 3 additions & 1 deletion ProcessMaker/Http/Controllers/CasesController.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -20,10 +20,12 @@ class CasesController extends Controller
*/
public function index()
{
$manager = app(ScreenBuilderManager::class);
event(new ScreenBuilderStarting($manager, 'FORM'));
$currentUser = Auth::user()->only(['id', 'username', 'fullname', 'firstname', 'lastname', 'avatar']);

// This is a temporary API the engine team will provide the new
return view('cases.casesMain', compact('currentUser'));
return view('cases.casesMain', compact('currentUser', 'manager'));
}

/**
Expand Down
2 changes: 1 addition & 1 deletion resources/js/components/TreeView.vue
Original file line numberDiff line numberDiff line change
Expand Up@@ -37,7 +37,7 @@ export default {
},
},
mounted() {
const jsonCrackEmbed = this.$refs.jsonCrackEmbed;
const { jsonCrackEmbed } = this.$refs;
const json = this.jsonData === "" ? "{}" : this.jsonData;
const options = {
theme: "light",
Expand Down
53 changes: 53 additions & 0 deletions resources/js/next/badPractices.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
/**
* BAD PRACTICES
*/

// Use multiple forms of an event bus.
window.ProcessMaker = {
EventBus: new Vue(),
events: new Vue()
};

// Force to overload the vue object
window.Vue.prototype.moment = moment;

// Load all statements in a single file bootstrap.js

// Overload the window.ProcessMaker variable without using the setGlobalVariable function

// Use local variables instead of window.ProcessMaker
window.ProcessMaker.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);
}
});
});
};

// Remove the saved search unneeded
class InjectJavascript
{
public function handle($request, Closure $next)
{
$response = $next($request);
if (get_class($response) !== Response::class) {
return $response;
}
$content = $response->getContent();
$tag = '';
$tag .= '<script src="' . mix('/js/addSaveButton.js', 'vendor/processmaker/packages/package-savedsearch') . '"></script>';
$tag .= '<script src="' . mix('/js/listenForRecounts.js', 'vendor/processmaker/packages/package-savedsearch') . '"></script>';
$content = str_replace('</body>', $tag . "\n</body>", $content);
$response->setContent($content);

return $response;
}
}

112 changes: 112 additions & 0 deletions resources/js/next/components/index.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
import { getGlobalVariable } from "../globalVariables";

// Multiselect

const Vue = getGlobalVariable("Vue");

const pmComponents = {
// Components folder
AvatarImage: () => import("../../components/AvatarImage.vue"),
Breadcrumbs: () => import("../../components/Breadcrumbs.vue"),
Confirm: () => import("../../components/Confirm.vue"),
CustomActions: () => import("../../components/CustomActions.vue"),
DetailRow: () => import("../../components/DetailRow.vue"),
FilterBar: () => import("../../components/FilterBar.vue"),
Menu: () => import("../../components/Menu.vue"),
Message: () => import("../../components/Message.vue"),
NavbarProfile: () => import("../../components/NavbarProfile.vue"),
PMBadgesFilters: () => import("../../components/PMBadgesFilters.vue"),
PMDatetimePicker: () => import("../../components/PMDatetimePicker.vue"),
PMDropdownSuggest: () => import("../../components/PMDropdownSuggest.vue"),
PMFloatingButtons: () => import("../../components/PMFloatingButtons.vue"),
PMFormSelectSuggest: () => import("../../components/PMFormSelectSuggest.vue"),
PMMessageResults: () => import("../../components/PMMessageResults.vue"),
PMMessageScreen: () => import("../../components/PMMessageScreen.vue"),
PMPanelWithCustomHeader: () => import("../../components/PMPanelWithCustomHeader.vue"),
PMPopoverConfirmation: () => import("../../components/PMPopoverConfirmation.vue"),
PMSearchBar: () => import("../../components/PMSearchBar.vue"),
PMTable: () => import("../../components/PMTable.vue"),
PMTabs: () => import("../../components/PMTabs.vue"),
Recommendations: () => import("../../components/Recommendations.vue"),
SelectFromApi: () => import("../../components/SelectFromApi.vue"),
SelectLanguage: () => import("../../components/SelectLanguage.vue"),
SelectScreen: () => import("../../components/SelectScreen.vue"),
SelectStatus: () => import("../../components/SelectStatus.vue"),
SelectUser: () => import("../../components/SelectUser.vue"),
SelectUserGroup: () => import("../../components/SelectUserGroup.vue"),
Session: () => import("../../components/Session.vue"),
Sidebaricon: () => import("../../components/Sidebaricon.vue"),
Timeline: () => import("../../components/Timeline.vue"),
TimelineItem: () => import("../../components/TimelineItem.vue"),
TreeView: () => import("../../components/TreeView.vue"),
// Shared components folder
AddToBundle: () => import("../../components/shared/AddToBundle"),
AddToProjectModal: () => import("../../components/shared/AddToProjectModal"),
AssetDependentTreeModal: () => import("../../components/shared/AssetDependentTreeModal.vue"),
AssetTreeModal: () => import("../../components/shared/AssetTreeModal.vue"),
BackendSelect: () => import("../../components/shared/BackendSelect.vue"),
BasicSearch: () => import("../../components/shared/BasicSearch.vue"),
CategorySelect: () => import("../../components/shared/CategorySelect.vue"),
ChangeLog: () => import("../../components/shared/ChangeLog.vue"),
ColorSchemeSelector: () => import("../../components/shared/ColorSchemeSelector.vue"),
Column: () => import("../../components/shared/Column.vue"),
ColumnChooser: () => import("../../components/shared/ColumnChooser.vue"),
ColumnConfig: () => import("../../components/shared/ColumnConfig.vue"),
DataCard: () => import("../../components/shared/DataCard.vue"),
DataFormatSelector: () => import("../../components/shared/DataFormatSelector.vue"),
DataMaskSelector: () => import("../../components/shared/DataMaskSelector.vue"),
DataNode: () => import("../../components/shared/DataNode.vue"),
DataTree: () => import("../../components/shared/DataTree.vue"),
DownloadSvgButton: () => import("../../components/shared/DownloadSvgButton.vue"),
DraggableFileUpload: () => import("../../components/shared/DraggableFileUpload.vue"),
EllipsisMenu: () => import("../../components/shared/EllipsisMenu.vue"),
FileUploadButton: () => import("../../components/shared/FileUploadButton.vue"),
FilterTable: () => import("../../components/shared/FilterTable.vue"),
IconDropdown: () => import("../../components/shared/IconDropdown.vue"),
IconSelector: () => import("../../components/shared/IconSelector.vue"),
InputImageCarousel: () => import("../../components/shared/InputImageCarousel.vue"),
LaunchpadSettingsModal: () => import("../../components/shared/LaunchpadSettingsModal.vue"),
Modal: () => import("../../components/shared/Modal.vue"),
ModalSaveVersion: () => import("../../components/shared/ModalSaveVersion.vue"),
MultiThumbnailFileUploader: () => import("../../components/shared/MultiThumbnailFileUploader.vue"),
PaginationTable: () => import("../../components/shared/PaginationTable.vue"),
PmqlInput: () => import("../../components/shared/PmqlInput.vue"),
PmqlInputFilters: () => import("../../components/shared/PmqlInputFilters.vue"),
ProjectSelect: () => import("../../components/shared/ProjectSelect.vue"),
PTab: () => import("../../components/shared/PTab.vue"),
PTabs: () => import("../../components/shared/PTabs.vue"),
Required: () => import("../../components/shared/Required.vue"),
SidebarButton: () => import("../../components/shared/SidebarButton.vue"),
SidebarNav: () => import("../../components/shared/SidebarNav.vue"),
SliderWithInput: () => import("../../components/shared/SliderWithInput.vue"),
// Common components folder
DataTreeToggle: () => import("../../components/common/data-tree-toggle.vue"),
// Tasks components folder
MobileTasks: () => import("../../tasks/components/MobileTasks.vue"),
NavbarTaskMobile: () => import("../../tasks/components/NavbarTaskMobile.vue"),
QuickFillPreview: () => import("../../tasks/components/QuickFillPreview.vue"),
ReassignMobileModal: () => import("../../tasks/components/ReassignMobileModal.vue"),
SplitpaneContainer: () => import("../../tasks/components/SplitpaneContainer.vue"),
TaskDetailsMobile: () => import("../../tasks/components/TaskDetailsMobile.vue"),
TaskListRowButtons: () => import("../../tasks/components/TaskListRowButtons.vue"),
TaskLoading: () => import("../../tasks/components/TaskLoading.vue"),
TaskSaveNotification: () => import("../../tasks/components/TaskSaveNotification.vue"),
TaskSavePanel: () => import("../../tasks/components/TaskSavePanel.vue"),
TasksHome: () => import("../../tasks/components/TasksHome.vue"),
TasksList: () => import("../../tasks/components/TasksList.vue"),
TasksListCounter: () => import("../../tasks/components/TasksListCounter.vue"),
TasksPreview: () => import("../../tasks/components/TasksPreview.vue"),
TaskTooltip: () => import("../../tasks/components/TaskTooltip.vue"),
TaskView: () => import("../../tasks/components/TaskView.vue"),
};

Object.entries(pmComponents).forEach(([key, component]) => {
Vue.component(key, component);
});

// Multiselect
Vue.component("Multiselect", (resolve, reject) => {
import("@processmaker/vue-multiselect").then((Multiselect) => {
resolve(Multiselect.Multiselect);
}).catch(reject);
});
6 changes: 6 additions & 0 deletions resources/js/next/config/accesibility.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
import AccessibilityMixin from "./components/common/mixins/accessibility";
import { getGlobalVariable } from "../globalVariables";

const Vue = getGlobalVariable("Vue");

Vue.mixin(AccessibilityMixin);
59 changes: 59 additions & 0 deletions resources/js/next/config/i18n.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
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";
import { setGlobalPMVariable, getGlobalVariable } from "../globalVariables";

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,
);

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
nsSeparator: false,
keySeparator: false,
parseMissingKeyHandler(value) {
if (!translationsLoaded) { return value; }
// Report that a translation is missing
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" },
],
},
});

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);
36 changes: 36 additions & 0 deletions resources/js/next/config/notifications.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
import { getGlobalPMVariable, setGlobalPMVariable } from "../globalVariables";

const apiClient = getGlobalPMVariable("apiClient");

const notifications = [];

const pushNotification = (notification) => {
if (notifications.filter((x) => x.id === notification).length === 0) {
notifications.push(notification);
}
};

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);
});

urls.forEach((url) => {
const messageIndex = notifications.findIndex((x) => x.url === url);
if (messageIndex >= 0) {
removeNotifications(notifications[messageIndex].id);
}
});
});

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);
9 changes: 9 additions & 0 deletions resources/js/next/config/openAI.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
import { setGlobalPMVariable } from "../globalVariables";

const openAi = document.head.querySelector("meta[name=\"open-ai-nlq-to-pmql\"]");

setGlobalPMVariable("openAi", openAi ? {
enabled: openAi.content,
} : {
enabled: false,
});
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
4 changes: 3 additions & 1 deletion ProcessMaker/Http/Controllers/CasesController.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -20,10 +20,12 @@ class CasesController extends Controller
*/
public function index()
{
$manager = app(ScreenBuilderManager::class);
event(new ScreenBuilderStarting($manager, 'FORM'));
$currentUser = Auth::user()->only(['id', 'username', 'fullname', 'firstname', 'lastname', 'avatar']);

// This is a temporary API the engine team will provide the new
return view('cases.casesMain', compact('currentUser'));
return view('cases.casesMain', compact('currentUser', 'manager'));
}

/**
Expand Down
2 changes: 1 addition & 1 deletion resources/js/components/TreeView.vue
Original file line numberDiff line numberDiff line change
Expand Up@@ -37,7 +37,7 @@ export default {
},
},
mounted() {
const jsonCrackEmbed = this.$refs.jsonCrackEmbed;
const { jsonCrackEmbed } = this.$refs;
const json = this.jsonData === "" ? "{}" : this.jsonData;
const options = {
theme: "light",
Expand Down
53 changes: 53 additions & 0 deletions resources/js/next/badPractices.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
/**
* BAD PRACTICES
*/

// Use multiple forms of an event bus.
window.ProcessMaker = {
EventBus: new Vue(),
events: new Vue()
};

// Force to overload the vue object
window.Vue.prototype.moment = moment;

// Load all statements in a single file bootstrap.js

// Overload the window.ProcessMaker variable without using the setGlobalVariable function

// Use local variables instead of window.ProcessMaker
window.ProcessMaker.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);
}
});
});
};

// Remove the saved search unneeded
class InjectJavascript
{
public function handle($request, Closure $next)
{
$response = $next($request);
if (get_class($response) !== Response::class) {
return $response;
}
$content = $response->getContent();
$tag = '';
$tag .= '<script src="' . mix('/js/addSaveButton.js', 'vendor/processmaker/packages/package-savedsearch') . '"></script>';
$tag .= '<script src="' . mix('/js/listenForRecounts.js', 'vendor/processmaker/packages/package-savedsearch') . '"></script>';
$content = str_replace('</body>', $tag . "\n</body>", $content);
$response->setContent($content);

return $response;
}
}

112 changes: 112 additions & 0 deletions resources/js/next/components/index.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
import { getGlobalVariable } from "../globalVariables";

// Multiselect

const Vue = getGlobalVariable("Vue");

const pmComponents = {
// Components folder
AvatarImage: () => import("../../components/AvatarImage.vue"),
Breadcrumbs: () => import("../../components/Breadcrumbs.vue"),
Confirm: () => import("../../components/Confirm.vue"),
CustomActions: () => import("../../components/CustomActions.vue"),
DetailRow: () => import("../../components/DetailRow.vue"),
FilterBar: () => import("../../components/FilterBar.vue"),
Menu: () => import("../../components/Menu.vue"),
Message: () => import("../../components/Message.vue"),
NavbarProfile: () => import("../../components/NavbarProfile.vue"),
PMBadgesFilters: () => import("../../components/PMBadgesFilters.vue"),
PMDatetimePicker: () => import("../../components/PMDatetimePicker.vue"),
PMDropdownSuggest: () => import("../../components/PMDropdownSuggest.vue"),
PMFloatingButtons: () => import("../../components/PMFloatingButtons.vue"),
PMFormSelectSuggest: () => import("../../components/PMFormSelectSuggest.vue"),
PMMessageResults: () => import("../../components/PMMessageResults.vue"),
PMMessageScreen: () => import("../../components/PMMessageScreen.vue"),
PMPanelWithCustomHeader: () => import("../../components/PMPanelWithCustomHeader.vue"),
PMPopoverConfirmation: () => import("../../components/PMPopoverConfirmation.vue"),
PMSearchBar: () => import("../../components/PMSearchBar.vue"),
PMTable: () => import("../../components/PMTable.vue"),
PMTabs: () => import("../../components/PMTabs.vue"),
Recommendations: () => import("../../components/Recommendations.vue"),
SelectFromApi: () => import("../../components/SelectFromApi.vue"),
SelectLanguage: () => import("../../components/SelectLanguage.vue"),
SelectScreen: () => import("../../components/SelectScreen.vue"),
SelectStatus: () => import("../../components/SelectStatus.vue"),
SelectUser: () => import("../../components/SelectUser.vue"),
SelectUserGroup: () => import("../../components/SelectUserGroup.vue"),
Session: () => import("../../components/Session.vue"),
Sidebaricon: () => import("../../components/Sidebaricon.vue"),
Timeline: () => import("../../components/Timeline.vue"),
TimelineItem: () => import("../../components/TimelineItem.vue"),
TreeView: () => import("../../components/TreeView.vue"),
// Shared components folder
AddToBundle: () => import("../../components/shared/AddToBundle"),
AddToProjectModal: () => import("../../components/shared/AddToProjectModal"),
AssetDependentTreeModal: () => import("../../components/shared/AssetDependentTreeModal.vue"),
AssetTreeModal: () => import("../../components/shared/AssetTreeModal.vue"),
BackendSelect: () => import("../../components/shared/BackendSelect.vue"),
BasicSearch: () => import("../../components/shared/BasicSearch.vue"),
CategorySelect: () => import("../../components/shared/CategorySelect.vue"),
ChangeLog: () => import("../../components/shared/ChangeLog.vue"),
ColorSchemeSelector: () => import("../../components/shared/ColorSchemeSelector.vue"),
Column: () => import("../../components/shared/Column.vue"),
ColumnChooser: () => import("../../components/shared/ColumnChooser.vue"),
ColumnConfig: () => import("../../components/shared/ColumnConfig.vue"),
DataCard: () => import("../../components/shared/DataCard.vue"),
DataFormatSelector: () => import("../../components/shared/DataFormatSelector.vue"),
DataMaskSelector: () => import("../../components/shared/DataMaskSelector.vue"),
DataNode: () => import("../../components/shared/DataNode.vue"),
DataTree: () => import("../../components/shared/DataTree.vue"),
DownloadSvgButton: () => import("../../components/shared/DownloadSvgButton.vue"),
DraggableFileUpload: () => import("../../components/shared/DraggableFileUpload.vue"),
EllipsisMenu: () => import("../../components/shared/EllipsisMenu.vue"),
FileUploadButton: () => import("../../components/shared/FileUploadButton.vue"),
FilterTable: () => import("../../components/shared/FilterTable.vue"),
IconDropdown: () => import("../../components/shared/IconDropdown.vue"),
IconSelector: () => import("../../components/shared/IconSelector.vue"),
InputImageCarousel: () => import("../../components/shared/InputImageCarousel.vue"),
LaunchpadSettingsModal: () => import("../../components/shared/LaunchpadSettingsModal.vue"),
Modal: () => import("../../components/shared/Modal.vue"),
ModalSaveVersion: () => import("../../components/shared/ModalSaveVersion.vue"),
MultiThumbnailFileUploader: () => import("../../components/shared/MultiThumbnailFileUploader.vue"),
PaginationTable: () => import("../../components/shared/PaginationTable.vue"),
PmqlInput: () => import("../../components/shared/PmqlInput.vue"),
PmqlInputFilters: () => import("../../components/shared/PmqlInputFilters.vue"),
ProjectSelect: () => import("../../components/shared/ProjectSelect.vue"),
PTab: () => import("../../components/shared/PTab.vue"),
PTabs: () => import("../../components/shared/PTabs.vue"),
Required: () => import("../../components/shared/Required.vue"),
SidebarButton: () => import("../../components/shared/SidebarButton.vue"),
SidebarNav: () => import("../../components/shared/SidebarNav.vue"),
SliderWithInput: () => import("../../components/shared/SliderWithInput.vue"),
// Common components folder
DataTreeToggle: () => import("../../components/common/data-tree-toggle.vue"),
// Tasks components folder
MobileTasks: () => import("../../tasks/components/MobileTasks.vue"),
NavbarTaskMobile: () => import("../../tasks/components/NavbarTaskMobile.vue"),
QuickFillPreview: () => import("../../tasks/components/QuickFillPreview.vue"),
ReassignMobileModal: () => import("../../tasks/components/ReassignMobileModal.vue"),
SplitpaneContainer: () => import("../../tasks/components/SplitpaneContainer.vue"),
TaskDetailsMobile: () => import("../../tasks/components/TaskDetailsMobile.vue"),
TaskListRowButtons: () => import("../../tasks/components/TaskListRowButtons.vue"),
TaskLoading: () => import("../../tasks/components/TaskLoading.vue"),
TaskSaveNotification: () => import("../../tasks/components/TaskSaveNotification.vue"),
TaskSavePanel: () => import("../../tasks/components/TaskSavePanel.vue"),
TasksHome: () => import("../../tasks/components/TasksHome.vue"),
TasksList: () => import("../../tasks/components/TasksList.vue"),
TasksListCounter: () => import("../../tasks/components/TasksListCounter.vue"),
TasksPreview: () => import("../../tasks/components/TasksPreview.vue"),
TaskTooltip: () => import("../../tasks/components/TaskTooltip.vue"),
TaskView: () => import("../../tasks/components/TaskView.vue"),
};

Object.entries(pmComponents).forEach(([key, component]) => {
Vue.component(key, component);
});

// Multiselect
Vue.component("Multiselect", (resolve, reject) => {
import("@processmaker/vue-multiselect").then((Multiselect) => {
resolve(Multiselect.Multiselect);
}).catch(reject);
});
6 changes: 6 additions & 0 deletions resources/js/next/config/accesibility.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
import AccessibilityMixin from "./components/common/mixins/accessibility";
import { getGlobalVariable } from "../globalVariables";

const Vue = getGlobalVariable("Vue");

Vue.mixin(AccessibilityMixin);
59 changes: 59 additions & 0 deletions resources/js/next/config/i18n.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
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";
import { setGlobalPMVariable, getGlobalVariable } from "../globalVariables";

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,
);

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
nsSeparator: false,
keySeparator: false,
parseMissingKeyHandler(value) {
if (!translationsLoaded) { return value; }
// Report that a translation is missing
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" },
],
},
});

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);
36 changes: 36 additions & 0 deletions resources/js/next/config/notifications.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
import { getGlobalPMVariable, setGlobalPMVariable } from "../globalVariables";

const apiClient = getGlobalPMVariable("apiClient");

const notifications = [];

const pushNotification = (notification) => {
if (notifications.filter((x) => x.id === notification).length === 0) {
notifications.push(notification);
}
};

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);
});

urls.forEach((url) => {
const messageIndex = notifications.findIndex((x) => x.url === url);
if (messageIndex >= 0) {
removeNotifications(notifications[messageIndex].id);
}
});
});

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);
9 changes: 9 additions & 0 deletions resources/js/next/config/openAI.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
import { setGlobalPMVariable } from "../globalVariables";

const openAi = document.head.querySelector("meta[name=\"open-ai-nlq-to-pmql\"]");

setGlobalPMVariable("openAi", openAi ? {
enabled: openAi.content,
} : {
enabled: false,
});
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
4 changes: 3 additions & 1 deletion ProcessMaker/Http/Controllers/CasesController.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -20,10 +20,12 @@ class CasesController extends Controller
*/
public function index()
{
$manager = app(ScreenBuilderManager::class);
event(new ScreenBuilderStarting($manager, 'FORM'));
$currentUser = Auth::user()->only(['id', 'username', 'fullname', 'firstname', 'lastname', 'avatar']);

// This is a temporary API the engine team will provide the new
return view('cases.casesMain', compact('currentUser'));
return view('cases.casesMain', compact('currentUser', 'manager'));
}

/**
Expand Down
2 changes: 1 addition & 1 deletion resources/js/components/TreeView.vue
Original file line numberDiff line numberDiff line change
Expand Up@@ -37,7 +37,7 @@ export default {
},
},
mounted() {
const jsonCrackEmbed = this.$refs.jsonCrackEmbed;
const { jsonCrackEmbed } = this.$refs;
const json = this.jsonData === "" ? "{}" : this.jsonData;
const options = {
theme: "light",
Expand Down
53 changes: 53 additions & 0 deletions resources/js/next/badPractices.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
/**
* BAD PRACTICES
*/

// Use multiple forms of an event bus.
window.ProcessMaker = {
EventBus: new Vue(),
events: new Vue()
};

// Force to overload the vue object
window.Vue.prototype.moment = moment;

// Load all statements in a single file bootstrap.js

// Overload the window.ProcessMaker variable without using the setGlobalVariable function

// Use local variables instead of window.ProcessMaker
window.ProcessMaker.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);
}
});
});
};

// Remove the saved search unneeded
class InjectJavascript
{
public function handle($request, Closure $next)
{
$response = $next($request);
if (get_class($response) !== Response::class) {
return $response;
}
$content = $response->getContent();
$tag = '';
$tag .= '<script src="' . mix('/js/addSaveButton.js', 'vendor/processmaker/packages/package-savedsearch') . '"></script>';
$tag .= '<script src="' . mix('/js/listenForRecounts.js', 'vendor/processmaker/packages/package-savedsearch') . '"></script>';
$content = str_replace('</body>', $tag . "\n</body>", $content);
$response->setContent($content);

return $response;
}
}

112 changes: 112 additions & 0 deletions resources/js/next/components/index.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
import { getGlobalVariable } from "../globalVariables";

// Multiselect

const Vue = getGlobalVariable("Vue");

const pmComponents = {
// Components folder
AvatarImage: () => import("../../components/AvatarImage.vue"),
Breadcrumbs: () => import("../../components/Breadcrumbs.vue"),
Confirm: () => import("../../components/Confirm.vue"),
CustomActions: () => import("../../components/CustomActions.vue"),
DetailRow: () => import("../../components/DetailRow.vue"),
FilterBar: () => import("../../components/FilterBar.vue"),
Menu: () => import("../../components/Menu.vue"),
Message: () => import("../../components/Message.vue"),
NavbarProfile: () => import("../../components/NavbarProfile.vue"),
PMBadgesFilters: () => import("../../components/PMBadgesFilters.vue"),
PMDatetimePicker: () => import("../../components/PMDatetimePicker.vue"),
PMDropdownSuggest: () => import("../../components/PMDropdownSuggest.vue"),
PMFloatingButtons: () => import("../../components/PMFloatingButtons.vue"),
PMFormSelectSuggest: () => import("../../components/PMFormSelectSuggest.vue"),
PMMessageResults: () => import("../../components/PMMessageResults.vue"),
PMMessageScreen: () => import("../../components/PMMessageScreen.vue"),
PMPanelWithCustomHeader: () => import("../../components/PMPanelWithCustomHeader.vue"),
PMPopoverConfirmation: () => import("../../components/PMPopoverConfirmation.vue"),
PMSearchBar: () => import("../../components/PMSearchBar.vue"),
PMTable: () => import("../../components/PMTable.vue"),
PMTabs: () => import("../../components/PMTabs.vue"),
Recommendations: () => import("../../components/Recommendations.vue"),
SelectFromApi: () => import("../../components/SelectFromApi.vue"),
SelectLanguage: () => import("../../components/SelectLanguage.vue"),
SelectScreen: () => import("../../components/SelectScreen.vue"),
SelectStatus: () => import("../../components/SelectStatus.vue"),
SelectUser: () => import("../../components/SelectUser.vue"),
SelectUserGroup: () => import("../../components/SelectUserGroup.vue"),
Session: () => import("../../components/Session.vue"),
Sidebaricon: () => import("../../components/Sidebaricon.vue"),
Timeline: () => import("../../components/Timeline.vue"),
TimelineItem: () => import("../../components/TimelineItem.vue"),
TreeView: () => import("../../components/TreeView.vue"),
// Shared components folder
AddToBundle: () => import("../../components/shared/AddToBundle"),
AddToProjectModal: () => import("../../components/shared/AddToProjectModal"),
AssetDependentTreeModal: () => import("../../components/shared/AssetDependentTreeModal.vue"),
AssetTreeModal: () => import("../../components/shared/AssetTreeModal.vue"),
BackendSelect: () => import("../../components/shared/BackendSelect.vue"),
BasicSearch: () => import("../../components/shared/BasicSearch.vue"),
CategorySelect: () => import("../../components/shared/CategorySelect.vue"),
ChangeLog: () => import("../../components/shared/ChangeLog.vue"),
ColorSchemeSelector: () => import("../../components/shared/ColorSchemeSelector.vue"),
Column: () => import("../../components/shared/Column.vue"),
ColumnChooser: () => import("../../components/shared/ColumnChooser.vue"),
ColumnConfig: () => import("../../components/shared/ColumnConfig.vue"),
DataCard: () => import("../../components/shared/DataCard.vue"),
DataFormatSelector: () => import("../../components/shared/DataFormatSelector.vue"),
DataMaskSelector: () => import("../../components/shared/DataMaskSelector.vue"),
DataNode: () => import("../../components/shared/DataNode.vue"),
DataTree: () => import("../../components/shared/DataTree.vue"),
DownloadSvgButton: () => import("../../components/shared/DownloadSvgButton.vue"),
DraggableFileUpload: () => import("../../components/shared/DraggableFileUpload.vue"),
EllipsisMenu: () => import("../../components/shared/EllipsisMenu.vue"),
FileUploadButton: () => import("../../components/shared/FileUploadButton.vue"),
FilterTable: () => import("../../components/shared/FilterTable.vue"),
IconDropdown: () => import("../../components/shared/IconDropdown.vue"),
IconSelector: () => import("../../components/shared/IconSelector.vue"),
InputImageCarousel: () => import("../../components/shared/InputImageCarousel.vue"),
LaunchpadSettingsModal: () => import("../../components/shared/LaunchpadSettingsModal.vue"),
Modal: () => import("../../components/shared/Modal.vue"),
ModalSaveVersion: () => import("../../components/shared/ModalSaveVersion.vue"),
MultiThumbnailFileUploader: () => import("../../components/shared/MultiThumbnailFileUploader.vue"),
PaginationTable: () => import("../../components/shared/PaginationTable.vue"),
PmqlInput: () => import("../../components/shared/PmqlInput.vue"),
PmqlInputFilters: () => import("../../components/shared/PmqlInputFilters.vue"),
ProjectSelect: () => import("../../components/shared/ProjectSelect.vue"),
PTab: () => import("../../components/shared/PTab.vue"),
PTabs: () => import("../../components/shared/PTabs.vue"),
Required: () => import("../../components/shared/Required.vue"),
SidebarButton: () => import("../../components/shared/SidebarButton.vue"),
SidebarNav: () => import("../../components/shared/SidebarNav.vue"),
SliderWithInput: () => import("../../components/shared/SliderWithInput.vue"),
// Common components folder
DataTreeToggle: () => import("../../components/common/data-tree-toggle.vue"),
// Tasks components folder
MobileTasks: () => import("../../tasks/components/MobileTasks.vue"),
NavbarTaskMobile: () => import("../../tasks/components/NavbarTaskMobile.vue"),
QuickFillPreview: () => import("../../tasks/components/QuickFillPreview.vue"),
ReassignMobileModal: () => import("../../tasks/components/ReassignMobileModal.vue"),
SplitpaneContainer: () => import("../../tasks/components/SplitpaneContainer.vue"),
TaskDetailsMobile: () => import("../../tasks/components/TaskDetailsMobile.vue"),
TaskListRowButtons: () => import("../../tasks/components/TaskListRowButtons.vue"),
TaskLoading: () => import("../../tasks/components/TaskLoading.vue"),
TaskSaveNotification: () => import("../../tasks/components/TaskSaveNotification.vue"),
TaskSavePanel: () => import("../../tasks/components/TaskSavePanel.vue"),
TasksHome: () => import("../../tasks/components/TasksHome.vue"),
TasksList: () => import("../../tasks/components/TasksList.vue"),
TasksListCounter: () => import("../../tasks/components/TasksListCounter.vue"),
TasksPreview: () => import("../../tasks/components/TasksPreview.vue"),
TaskTooltip: () => import("../../tasks/components/TaskTooltip.vue"),
TaskView: () => import("../../tasks/components/TaskView.vue"),
};

Object.entries(pmComponents).forEach(([key, component]) => {
Vue.component(key, component);
});

// Multiselect
Vue.component("Multiselect", (resolve, reject) => {
import("@processmaker/vue-multiselect").then((Multiselect) => {
resolve(Multiselect.Multiselect);
}).catch(reject);
});
6 changes: 6 additions & 0 deletions resources/js/next/config/accesibility.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
import AccessibilityMixin from "./components/common/mixins/accessibility";
import { getGlobalVariable } from "../globalVariables";

const Vue = getGlobalVariable("Vue");

Vue.mixin(AccessibilityMixin);
59 changes: 59 additions & 0 deletions resources/js/next/config/i18n.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
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";
import { setGlobalPMVariable, getGlobalVariable } from "../globalVariables";

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,
);

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
nsSeparator: false,
keySeparator: false,
parseMissingKeyHandler(value) {
if (!translationsLoaded) { return value; }
// Report that a translation is missing
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" },
],
},
});

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);
36 changes: 36 additions & 0 deletions resources/js/next/config/notifications.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
import { getGlobalPMVariable, setGlobalPMVariable } from "../globalVariables";

const apiClient = getGlobalPMVariable("apiClient");

const notifications = [];

const pushNotification = (notification) => {
if (notifications.filter((x) => x.id === notification).length === 0) {
notifications.push(notification);
}
};

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);
});

urls.forEach((url) => {
const messageIndex = notifications.findIndex((x) => x.url === url);
if (messageIndex >= 0) {
removeNotifications(notifications[messageIndex].id);
}
});
});

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);
9 changes: 9 additions & 0 deletions resources/js/next/config/openAI.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
import { setGlobalPMVariable } from "../globalVariables";

const openAi = document.head.querySelector("meta[name=\"open-ai-nlq-to-pmql\"]");

setGlobalPMVariable("openAi", openAi ? {
enabled: openAi.content,
} : {
enabled: false,
});
Loading