The Bubble web UI in VueJS
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

557 lines
15 KiB

  1. /**
  2. * Copyright (c) 2020 Bubble, Inc. All rights reserved.
  3. * For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/
  4. */
  5. import { systemService } from '~/_services';
  6. import { util } from '~/_helpers';
  7. import { router } from '~/_router';
  8. import { account } from './account.module';
  9. import staticMessages from '~/_assets/messages.json';
  10. const state = {
  11. configs: {
  12. networkUuid: null,
  13. allowRegistration: false,
  14. paymentsEnabled: false,
  15. sageLauncher: false,
  16. bubbleNode: null,
  17. entityClasses: [],
  18. locales: ['en_US'],
  19. cloudConfigs: {},
  20. sslPort: null,
  21. locked: null,
  22. launchLock: null,
  23. promoCodePolicy: null,
  24. requireSendMetrics: null,
  25. awaitingRestore: false,
  26. restoreInProgress: false,
  27. support: {},
  28. securityLevels: null,
  29. jarVersion: null,
  30. jarUpgradeAvailable: null,
  31. },
  32. entityConfigs: {},
  33. searchResults: [],
  34. status: {
  35. activating: false,
  36. searching: false,
  37. creatingEntity: false,
  38. modelSetupInProgress: false,
  39. },
  40. activated: null,
  41. error: null,
  42. messages: {
  43. durationToMillis: function(count, units) {
  44. if (typeof count === 'undefined' || count === null || count === '')
  45. return null;
  46. return (
  47. parseInt(count) *
  48. parseInt(state.messages['time_duration_' + units + '_factor'])
  49. );
  50. },
  51. millisToDuration: function(ms) {
  52. const durations = state.timeDurationOptionsReversed;
  53. for (let i = 0; i < durations.length; i++) {
  54. const durationMillis = parseInt(
  55. state.messages['time_duration_' + durations[i] + '_factor']
  56. );
  57. if (
  58. ms >= durationMillis &&
  59. (ms % durationMillis === 0 || i === durations.length - 1)
  60. ) {
  61. return { count: parseInt(ms) / durationMillis, units: durations[i] };
  62. }
  63. }
  64. return { count: parseInt(ms), units: '' };
  65. },
  66. ...staticMessages,
  67. },
  68. messageGroupsLoaded: [],
  69. countries: [],
  70. locales: [],
  71. timezones: [],
  72. detectedTimezone: null,
  73. detectedLocale: null,
  74. accountDeletionOptions: [],
  75. timeDurationOptions: [],
  76. timeDurationOptionsReversed: [],
  77. contactTypes: [],
  78. appLinks: null,
  79. upgradeCheck: null,
  80. upgrading: null,
  81. };
  82. const actions = {
  83. loadIsActivated({ commit }) {
  84. commit('loadIsActivatedRequest');
  85. systemService
  86. .loadIsActivated()
  87. .then(
  88. (activated) => commit('loadIsActivatedSuccess', activated),
  89. (error) => commit('loadIsActivatedFailure', error)
  90. );
  91. },
  92. activate({ commit }, { activation, messages, errors }) {
  93. commit('activateRequest');
  94. systemService.activate(activation, messages, errors).then(
  95. (admin) => {
  96. commit('activateSuccess', admin);
  97. router.replace('/');
  98. },
  99. (error) => commit('activateFailure', error)
  100. );
  101. },
  102. loadSystemConfigs({ commit }) {
  103. commit('loadSystemConfigsRequest');
  104. systemService
  105. .loadSystemConfigs()
  106. .then(
  107. (configs) => commit('loadSystemConfigsSuccess', configs),
  108. (error) => commit('loadSystemConfigsFailure', error)
  109. );
  110. },
  111. loadEntityConfigs({ commit }) {
  112. commit('loadEntityConfigsRequest');
  113. systemService
  114. .loadEntityConfigs()
  115. .then(
  116. (configs) => commit('loadEntityConfigsSuccess', configs),
  117. (error) => commit('loadEntityConfigsFailure', error)
  118. );
  119. },
  120. search({ commit }, type, query) {
  121. commit('searchRequest');
  122. systemService
  123. .search(type, query)
  124. .then(
  125. (results) => commit('searchSuccess', { type, query, results }),
  126. (error) => commit('searchFailure', error)
  127. );
  128. },
  129. createEntity({ commit }, { entityConfig, json, messages, errors }) {
  130. commit('createEntityRequest');
  131. systemService
  132. .createEntity(entityConfig, json, messages, errors)
  133. .then(
  134. (entity) => commit('createEntitySuccess', entity),
  135. (error) => commit('createEntityFailure', error)
  136. );
  137. },
  138. modelSetup({ commit }, { file, messages, errors }) {
  139. commit('modelSetupRequest');
  140. systemService
  141. .modelSetup(file, messages, errors)
  142. .then(
  143. (ok) => commit('modelSetupSuccess'),
  144. (errors) => commit('modelSetupFailure', errors)
  145. );
  146. },
  147. loadMessages({ commit }, group, locale) {
  148. commit('loadMessagesRequest');
  149. systemService
  150. .loadMessages(group, locale)
  151. .then(
  152. (messages) => commit('loadMessagesSuccess', { group, messages }),
  153. (error) => commit('loadMessagesFailure', error)
  154. );
  155. },
  156. loadTimezones({ commit }) {
  157. commit('loadTimezonesRequest');
  158. systemService
  159. .loadTimezones()
  160. .then(
  161. (timezones) => commit('loadTimezonesSuccess', timezones),
  162. (error) => commit('loadTimezonesFailure', error)
  163. );
  164. },
  165. detectTimezone({ commit }) {
  166. commit('detectTimezoneRequest');
  167. systemService
  168. .detectTimezone()
  169. .then(
  170. (timezone) => commit('detectTimezoneSuccess', timezone),
  171. (error) => commit('detectTimezoneFailure', error)
  172. );
  173. },
  174. detectLocale({ commit }) {
  175. commit('detectLocaleRequest');
  176. systemService
  177. .detectLocale()
  178. .then(
  179. (locales) => commit('detectLocaleSuccess', locales),
  180. (error) => commit('detectLocaleFailure', error)
  181. );
  182. },
  183. getAppLinks({ commit }, locale) {
  184. commit('getAppLinksRequest');
  185. systemService
  186. .getAppLinks(locale)
  187. .then(
  188. (links) => commit('getAppLinksSuccess', links),
  189. (error) => commit('getAppLinksFailure', error)
  190. );
  191. },
  192. checkForUpgrade({ commit }) {
  193. if (state.upgradeCheck === null) {
  194. commit('checkForUpgradeRequest');
  195. systemService
  196. .checkForUpgrade()
  197. .then(
  198. (configs) => commit('checkForUpgradeSuccess', configs),
  199. (error) => commit('checkForUpgradeFailure', error)
  200. );
  201. } else {
  202. console.log(
  203. 'checkForUpgrade: already checked (' +
  204. state.upgradeCheck +
  205. '), not checking again'
  206. );
  207. }
  208. },
  209. upgrade({ commit }) {
  210. commit('upgradeRequest');
  211. systemService
  212. .upgrade()
  213. .then(
  214. (configs) => commit('upgradeSuccess', configs),
  215. (error) => commit('upgradeFailure', error)
  216. );
  217. },
  218. upgradeComplete({ commit }) {
  219. commit('upgradeCompleteRequest');
  220. commit('upgradeCompleteSuccess');
  221. },
  222. };
  223. const getters = {
  224. dashboardApps: function() {
  225. const configs = state.configs;
  226. const messages = state.messages;
  227. const isAdmin =
  228. account.state.user !== null && account.state.user.admin === true;
  229. const dashApps = [];
  230. dashApps.push({
  231. href: '/me',
  232. title: messages.label_menu_account,
  233. icon: messages.label_menu_account_icon,
  234. index: 0,
  235. });
  236. if (configs.sageLauncher) {
  237. dashApps.push({
  238. href: '/bubbles',
  239. title: messages.label_menu_networks,
  240. icon: messages.label_menu_networks_icon,
  241. index: 1,
  242. });
  243. } else {
  244. if (isAdmin) {
  245. dashApps.push({
  246. href: '/bubble/' + configs.networkUuid,
  247. title: messages.label_menu_network,
  248. icon: messages.label_menu_networks_icon,
  249. index: 1,
  250. });
  251. }
  252. dashApps.push({
  253. href: '/devices',
  254. title: messages.label_menu_devices,
  255. icon: messages.label_menu_devices_icon,
  256. index: 2,
  257. });
  258. dashApps.push({
  259. href: '/apps',
  260. title: messages.label_menu_apps,
  261. icon: messages.label_menu_apps_icon,
  262. index: 3,
  263. });
  264. }
  265. if (isAdmin && configs.sageLauncher) {
  266. const adminApps = [
  267. {
  268. href: '/',
  269. title: messages.label_menu_dashboard,
  270. icon: messages.label_menu_dashboard_icon,
  271. index: 0,
  272. },
  273. ];
  274. adminApps.push({
  275. href: '/admin/accounts',
  276. title: messages.label_menu_admin_users,
  277. icon: messages.label_menu_admin_users_icon,
  278. index: 1,
  279. });
  280. adminApps.push({
  281. href: '/admin/model',
  282. title: messages.label_menu_admin_model,
  283. icon: messages.label_menu_admin_model_icon,
  284. index: 2,
  285. });
  286. adminApps.push({
  287. href: '/admin/bubbles',
  288. title: messages.label_menu_admin_networks,
  289. icon: messages.label_menu_admin_networks_icon,
  290. index: 3,
  291. });
  292. if (configs.paymentsEnabled) {
  293. adminApps.push({
  294. href: '/admin/bills',
  295. title: messages.label_menu_admin_bills,
  296. icon: messages.label_menu_admin_bills_icon,
  297. index: 4,
  298. });
  299. }
  300. dashApps.push({
  301. href: '/?app=admin',
  302. title: messages.label_menu_admin,
  303. icon: messages.label_menu_admin_icon,
  304. index: 4,
  305. apps: adminApps,
  306. });
  307. } else if (isAdmin) {
  308. dashApps.push({
  309. href: '/admin/accounts',
  310. title: messages.label_menu_admin_users,
  311. icon: messages.label_menu_admin_users_icon,
  312. index: 4,
  313. });
  314. }
  315. dashApps.push({
  316. href: '/logout',
  317. title: messages.label_menu_logout,
  318. icon: messages.label_menu_logout_icon,
  319. index: 1000,
  320. });
  321. return dashApps;
  322. },
  323. promoCodesEnabled: function() {
  324. return (
  325. state.promoCodePolicy === 'required' ||
  326. state.promoCodePolicy === 'optional'
  327. );
  328. },
  329. promoCodeRequired: function() {
  330. return state.promoCodePolicy === 'required';
  331. },
  332. };
  333. const mutations = {
  334. loadIsActivatedRequest(state) {},
  335. loadIsActivatedSuccess(state, activated) {
  336. state.activated = activated;
  337. },
  338. loadIsActivatedFailure(state, error) {
  339. state.error = error;
  340. },
  341. activateRequest(state) {
  342. state.status.activating = true;
  343. },
  344. activateSuccess(state, admin) {
  345. state.status.activating = false;
  346. state.activated = true;
  347. state.user = admin;
  348. state.status = { loggedIn: admin !== null };
  349. localStorage.setItem(util.USER_KEY, JSON.stringify(admin));
  350. },
  351. activateFailure(state, error) {
  352. state.status.activating = false;
  353. state.error = error;
  354. },
  355. loadSystemConfigsRequest(state) {},
  356. loadSystemConfigsSuccess(state, configs) {
  357. state.configs = configs;
  358. },
  359. loadSystemConfigsFailure(state, error) {
  360. state.error = error;
  361. },
  362. loadEntityConfigsRequest(state) {},
  363. loadEntityConfigsSuccess(state, configs) {
  364. // console.log('loadEntityConfigsSuccess: received configs='+JSON.stringify(configs));
  365. const newConfigs = {};
  366. for (let i = 0; i < configs.length; i++) {
  367. for (let j = 0; j < configs[i].names.length; j++) {
  368. newConfigs[configs[i].names[j]] = configs[i].entityConfig;
  369. }
  370. }
  371. state.entityConfigs = newConfigs;
  372. },
  373. loadEntityConfigsFailure(state, error) {
  374. state.error = error;
  375. },
  376. searchRequest(state) {
  377. state.status.searching = true;
  378. },
  379. searchSuccess(state, { type, query, results }) {
  380. state.status.searching = false;
  381. state.searchResults = results;
  382. },
  383. searchFailure(state, error) {
  384. state.status.searching = false;
  385. state.error = error;
  386. },
  387. createEntityRequest(state) {
  388. state.status.creatingEntity = true;
  389. },
  390. createEntitySuccess(state, { entity }) {
  391. state.status.creatingEntity = false;
  392. },
  393. createEntityFailure(state, error) {
  394. state.status.creatingEntity = false;
  395. state.error = error;
  396. },
  397. modelSetupRequest(state) {
  398. state.status.modelSetupInProgress = true;
  399. },
  400. modelSetupSuccess(state) {
  401. state.status.modelSetupInProgress = false;
  402. },
  403. modelSetupFailure(state, errors) {
  404. state.status.modelSetupInProgress = false;
  405. state.error = errors;
  406. },
  407. loadMessagesRequest(state) {},
  408. loadMessagesSuccess(state, { group, messages }) {
  409. if (state.messageGroupsLoaded.indexOf(group) === -1)
  410. state.messageGroupsLoaded.push(group);
  411. state.messages = util.addMessages(state.messages, messages);
  412. if (messages.country_codes) {
  413. const countries = [];
  414. const codes = messages.country_codes.split(',');
  415. for (let i = 0; i < codes.length; i++) {
  416. countries.push({
  417. code: codes[i],
  418. countryName: messages['country_' + codes[i]],
  419. });
  420. }
  421. state.countries = countries;
  422. }
  423. if (messages.locale_codes) {
  424. const locales = [];
  425. const codes = messages.locale_codes.split(',');
  426. for (let i = 0; i < codes.length; i++) {
  427. locales.push({
  428. localeCode: codes[i],
  429. localeName: messages['locale_' + codes[i]],
  430. });
  431. }
  432. state.locales = locales;
  433. }
  434. if (messages.field_label_policy_account_deletion_options) {
  435. state.accountDeletionOptions = messages.field_label_policy_account_deletion_options.split(
  436. ','
  437. );
  438. }
  439. if (messages.time_duration_options) {
  440. state.timeDurationOptions = messages.time_duration_options.split(',');
  441. state.timeDurationOptionsReversed = state.timeDurationOptions
  442. .slice()
  443. .reverse();
  444. }
  445. if (messages.field_label_policy_contact_type_options) {
  446. state.contactTypes = messages.field_label_policy_contact_type_options.split(
  447. ','
  448. );
  449. }
  450. },
  451. loadMessagesFailure(state, error) {
  452. state.error = error;
  453. },
  454. loadTimezonesRequest(state) {},
  455. loadTimezonesSuccess(state, timezones) {
  456. state.timezones = timezones;
  457. },
  458. loadTimezonesFailure(state, error) {
  459. state.error = error;
  460. },
  461. detectTimezoneRequest(state) {},
  462. detectTimezoneSuccess(state, detectedTimezone) {
  463. state.detectedTimezone = detectedTimezone;
  464. },
  465. detectTimezoneFailure(state, error) {
  466. state.error = error;
  467. },
  468. detectLocaleRequest(state) {},
  469. detectLocaleSuccess(state, detectedLocales) {
  470. if (detectedLocales.length) {
  471. for (let i = 0; i < detectedLocales.length; i++) {
  472. if (state.locales.indexOf(detectedLocales[i])) {
  473. state.detectedLocale = detectedLocales[i];
  474. return;
  475. }
  476. }
  477. }
  478. console.warn(
  479. 'detectLocaleSuccess: server detected locales ' +
  480. JSON.stringify(detectedLocales) +
  481. ' but none were found in locales list: ' +
  482. JSON.stringify(state.locales)
  483. );
  484. // keep current value, or assign default if no current value
  485. if (
  486. state.detectedLocale === null &&
  487. state.locales &&
  488. state.locales.length > 0
  489. )
  490. state.detectedLocale = state.locales[0];
  491. },
  492. detectLocaleFailure(state, error) {
  493. state.error = error;
  494. // keep current value, or assign default if no current value
  495. if (
  496. state.detectedLocale === null &&
  497. state.locales &&
  498. state.locales.length > 0
  499. )
  500. state.detectedLocale = state.locales[0];
  501. },
  502. getAppLinksRequest(state) {},
  503. getAppLinksSuccess(state, links) {
  504. state.appLinks = links;
  505. },
  506. getAppLinksFailure(state, error) {
  507. state.error = error;
  508. },
  509. checkForUpgradeRequest(state) {},
  510. checkForUpgradeSuccess(state, ok) {
  511. console.log('checkForUpgradeSuccess: ok');
  512. state.upgradeCheck = true;
  513. },
  514. checkForUpgradeFailure(state, error) {
  515. state.error = error;
  516. },
  517. upgradeRequest(state) {},
  518. upgradeSuccess(state, configs) {
  519. state.configs = configs;
  520. state.upgrading = true;
  521. },
  522. upgradeFailure(state, error) {
  523. state.error = error;
  524. },
  525. upgradeCompleteRequest(state) {},
  526. upgradeCompleteSuccess(state) {
  527. state.upgrading = false;
  528. },
  529. upgradeCompleteFailure(state, error) {
  530. state.upgrading = false;
  531. state.error = error;
  532. },
  533. };
  534. export const system = {
  535. namespaced: true,
  536. getters,
  537. state,
  538. actions,
  539. mutations,
  540. };