adminforth 1.2.87 → 1.2.89

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/dist/modules/restApi.js +9 -0
  2. package/dist/spa/.vscode/extensions.json +6 -0
  3. package/dist/spa/index.html +4 -4
  4. package/dist/spa/package-lock.json +6 -421
  5. package/dist/spa/package.json +2 -10
  6. package/dist/spa/public/favicon.ico +0 -0
  7. package/dist/spa/spa/.eslintrc.cjs +14 -0
  8. package/dist/spa/spa/.vscode/extensions.json +6 -0
  9. package/dist/spa/spa/README.md +39 -0
  10. package/dist/spa/spa/env.d.ts +1 -0
  11. package/dist/spa/spa/index.html +23 -0
  12. package/dist/spa/spa/package-lock.json +4573 -0
  13. package/dist/spa/spa/package.json +49 -0
  14. package/dist/spa/spa/postcss.config.js +6 -0
  15. package/dist/spa/spa/public/favicon.ico +0 -0
  16. package/dist/spa/spa/src/App.vue +314 -0
  17. package/dist/spa/spa/src/assets/base.css +0 -0
  18. package/dist/spa/spa/src/assets/logo.svg +19 -0
  19. package/dist/spa/spa/src/components/AcceptModal.vue +45 -0
  20. package/dist/spa/spa/src/components/Breadcrumbs.vue +40 -0
  21. package/dist/spa/spa/src/components/BreadcrumbsWithButtons.vue +26 -0
  22. package/dist/spa/spa/src/components/CustomDatePicker.vue +174 -0
  23. package/dist/spa/spa/src/components/CustomDateRangePicker.vue +218 -0
  24. package/dist/spa/spa/src/components/Dropdown.vue +168 -0
  25. package/dist/spa/spa/src/components/Filters.vue +222 -0
  26. package/dist/spa/spa/src/components/HelloWorld.vue +17 -0
  27. package/dist/spa/spa/src/components/MenuLink.vue +24 -0
  28. package/dist/spa/spa/src/components/ResourceForm.vue +289 -0
  29. package/dist/spa/{src → spa/src}/components/ResourceListTable.vue +3 -6
  30. package/dist/spa/spa/src/components/SingleSkeletLoader.vue +13 -0
  31. package/dist/spa/{src → spa/src}/components/Toast.vue +3 -9
  32. package/dist/spa/spa/src/components/ValueRenderer.vue +66 -0
  33. package/dist/spa/spa/src/components/icons/IconCalendar.vue +5 -0
  34. package/dist/spa/spa/src/components/icons/IconCommunity.vue +7 -0
  35. package/dist/spa/spa/src/components/icons/IconDocumentation.vue +7 -0
  36. package/dist/spa/spa/src/components/icons/IconEcosystem.vue +7 -0
  37. package/dist/spa/spa/src/components/icons/IconSupport.vue +7 -0
  38. package/dist/spa/spa/src/components/icons/IconTime.vue +5 -0
  39. package/dist/spa/spa/src/components/icons/IconTooling.vue +19 -0
  40. package/dist/spa/spa/src/index.scss +27 -0
  41. package/dist/spa/spa/src/main.ts +18 -0
  42. package/dist/spa/spa/src/router/index.ts +63 -0
  43. package/dist/spa/spa/src/stores/core.ts +144 -0
  44. package/dist/spa/spa/src/stores/modal.ts +48 -0
  45. package/dist/spa/spa/src/utils.ts +103 -0
  46. package/dist/spa/spa/src/views/CreateView.vue +156 -0
  47. package/dist/spa/spa/src/views/EditView.vue +157 -0
  48. package/dist/spa/spa/src/views/HomeView.vue +8 -0
  49. package/dist/spa/spa/src/views/ListView.vue +258 -0
  50. package/dist/spa/spa/src/views/LoginView.vue +143 -0
  51. package/dist/spa/spa/src/views/ResourceParent.vue +17 -0
  52. package/dist/spa/spa/src/views/ShowView.vue +184 -0
  53. package/dist/spa/spa/tailwind.config.js +17 -0
  54. package/dist/spa/spa/tsconfig.app.json +14 -0
  55. package/dist/spa/spa/tsconfig.json +11 -0
  56. package/dist/spa/spa/tsconfig.node.json +19 -0
  57. package/dist/spa/spa/vite.config.ts +56 -0
  58. package/dist/spa/src/App.vue +94 -236
  59. package/dist/spa/src/assets/base.css +0 -2
  60. package/dist/spa/src/assets/logo.svg +1 -19
  61. package/dist/spa/src/components/AcceptModal.vue +10 -3
  62. package/dist/spa/src/components/Breadcrumbs.vue +2 -3
  63. package/dist/spa/src/components/BreadcrumbsWithButtons.vue +1 -1
  64. package/dist/spa/src/components/CustomDatePicker.vue +10 -25
  65. package/dist/spa/src/components/CustomDateRangePicker.vue +5 -14
  66. package/dist/spa/src/components/Dropdown.vue +5 -19
  67. package/dist/spa/src/components/Filters.vue +38 -103
  68. package/dist/spa/src/components/MenuLink.vue +3 -3
  69. package/dist/spa/src/components/ResourceForm.vue +116 -205
  70. package/dist/spa/src/components/ValueRenderer.vue +8 -30
  71. package/dist/spa/src/index.scss +1 -2
  72. package/dist/spa/src/main.ts +2 -2
  73. package/dist/spa/src/router/index.ts +19 -29
  74. package/dist/spa/src/stores/core.ts +59 -65
  75. package/dist/spa/src/stores/modal.ts +3 -13
  76. package/dist/spa/src/utils.ts +10 -69
  77. package/dist/spa/src/views/CreateView.vue +16 -69
  78. package/dist/spa/src/views/EditView.vue +14 -68
  79. package/dist/spa/src/views/HomeView.vue +8 -0
  80. package/dist/spa/src/views/ListView.vue +371 -100
  81. package/dist/spa/src/views/LoginView.vue +29 -67
  82. package/dist/spa/src/views/ResourceParent.vue +2 -1
  83. package/dist/spa/src/views/ShowView.vue +25 -116
  84. package/dist/spa/tailwind.config.js +3 -8
  85. package/dist/spa/vite.config.ts +0 -13
  86. package/dist/types.js +30 -0
  87. package/modules/restApi.ts +14 -1
  88. package/package.json +1 -1
  89. package/spa/src/components/MenuLink.vue +3 -0
  90. package/types/AdminForthConfig.ts +6 -0
  91. package/dist/spa/src/types/AdminForthConfig.ts +0 -1477
  92. package/dist/spa/src/types/FrontendAPI.ts +0 -121
  93. /package/dist/spa/{public → spa/public}/assets/favicon.png +0 -0
  94. /package/dist/spa/{src → spa/src}/components/CustomRangePicker.vue +0 -0
  95. /package/dist/spa/{src → spa/src}/components/SkeleteLoader.vue +0 -0
  96. /package/dist/spa/{src → spa/src}/composables/useFrontendApi.ts +0 -0
  97. /package/dist/spa/{src → spa/src}/composables/useStores.ts +0 -0
  98. /package/dist/spa/{src → spa/src}/spa_types/core.ts +0 -0
  99. /package/dist/spa/{src → spa/src}/stores/filters.ts +0 -0
  100. /package/dist/spa/{src → spa/src}/stores/toast.ts +0 -0
  101. /package/dist/spa/{src → spa/src}/stores/user.ts +0 -0
@@ -1,105 +1,96 @@
1
1
  import { ref, computed } from 'vue'
2
2
  import { defineStore } from 'pinia'
3
3
  import { callAdminForthApi } from '@/utils';
4
- import type { AdminForthResource, AdminForthResourceColumn } from '@/types/AdminForthConfig';
5
- import type { Ref } from 'vue'
4
+ import router from '@/router';
5
+
6
+ async function findHomepage(menu) {
7
+ for (const item of menu) {
8
+ if (item.homepage) {
9
+ return item;
10
+ }
11
+ if (item.children) {
12
+ const res = findHomepage(item.children);
13
+ if (res) {
14
+ return res;
15
+ }
16
+ }
17
+ }
18
+ return null;
19
+ }
6
20
 
7
21
  export const useCoreStore = defineStore('core', () => {
8
- const resourceById: Ref<Object> = ref({});
22
+ const resourceById = ref([]);
9
23
  const menu = ref([]);
10
24
  const config = ref({});
11
- const record: Ref<any | null> = ref({});
12
- const resource: Ref<AdminForthResource | null> = ref(null);
13
-
14
- const resourceColumnsWithFilters = computed(() => {
15
- if (!resource.value) {
16
- return [];
17
- }
18
- return resource.value.columns.filter((col: AdminForthResourceColumn) => col.showIn?.includes('filter'));
19
- })
20
-
21
- const resourceOptions = ref(null);
25
+ const record = ref({});
26
+ const resourceColumns = ref(null);
22
27
  const resourceColumnsError = ref('');
23
28
  const resourceColumnsId = ref(null);
24
- const adminUser = ref(null);
29
+ const user = ref(null);
25
30
 
26
31
  async function fetchMenuAndResource() {
27
32
  const resp = await callAdminForthApi({
28
33
  path: '/get_base_config',
29
34
  method: 'GET',
30
35
  });
31
- if(!resp){
32
- return
33
- }
34
36
  menu.value = resp.menu;
35
- resourceById.value = resp.resources.reduce((acc: Object, resource: AdminForthResource) => {
37
+ resourceById.value = resp.resources.reduce((acc, resource) => {
36
38
  acc[resource.resourceId] = resource;
37
39
  return acc;
38
40
  }, {});
39
41
  config.value = resp.config;
40
- adminUser.value = resp.user;
41
- console.log('🌍 AdminForth v', resp.version);
42
+ user.value = resp.user;
43
+
44
+ // find homepage:true in menu recuresively
45
+ if (import.meta.env.DEV){
46
+ return
47
+ } else {
48
+ const homepage = await findHomepage(menu.value);
49
+ if (homepage) {
50
+ if (homepage.resourceId) {
51
+ // redirect to homepage
52
+ router.push({ name: 'resource-list', params: { resourceId: homepage.resourceId } });
53
+ } else {
54
+ // redirect to path
55
+ router.push(homepage.path);
56
+ }
57
+ }
58
+ }
42
59
  }
43
60
 
44
61
  async function fetchRecord({ resourceId, primaryKey }) {
45
62
  record.value = null;
46
-
47
- if (!resource.value) {
48
- throw new Error('Columns not fetched yet');
49
- }
50
63
 
51
- const respData = await callAdminForthApi({
52
- path: '/get_resource_data',
64
+ record.value = await callAdminForthApi({
65
+ path: '/get_record',
53
66
  method: 'POST',
54
67
  body: {
55
- source: 'show',
56
68
  resourceId: resourceId,
57
- filters: [
58
- {
59
- field: resource.value.columns.find((col: AdminForthResourceColumn) => col.primaryKey).name,
60
- operator: 'eq',
61
- value: primaryKey
62
- }
63
- ],
64
- sort: [],
65
- limit: 1,
66
- offset: 0
69
+ primaryKey: primaryKey,
67
70
  }
68
71
  });
69
-
70
- if (respData.error) {
71
- window.adminforth.alert({
72
- message: respData.error,
73
- variant: 'danger',
74
- timeout: 'unlimited'
75
- });
76
- record.value = {};
77
- } else {
78
- record.value = respData.data[0];
79
- }
80
-
81
72
  }
82
73
 
83
- async function fetchResourceFull({ resourceId }: { resourceId: string }) {
84
- if (resourceColumnsId.value === resourceId && resource.value) {
74
+ async function fetchColumns({ resourceId }) {
75
+ if (resourceColumnsId.value === resourceId && resourceColumns.value) {
85
76
  // already fetched
86
77
  return;
87
78
  }
88
79
  resourceColumnsId.value = resourceId;
80
+ resourceColumns.value = null;
89
81
  resourceColumnsError.value = '';
90
82
  const res = await callAdminForthApi({
91
- path: '/get_resource',
83
+ path: '/get_resource_columns',
92
84
  method: 'POST',
93
85
  body: {
94
86
  resourceId,
95
87
  }
96
- });
88
+ }
89
+ );
97
90
  if (res.error) {
98
91
  resourceColumnsError.value = res.error;
99
92
  } else {
100
- resourceById.value[resourceId] = res.resource;
101
- resource.value = res.resource;
102
- resourceOptions.value = res.resource.options;
93
+ resourceColumns.value = res.resource.columns;
103
94
  }
104
95
  }
105
96
 
@@ -111,16 +102,21 @@ export const useCoreStore = defineStore('core', () => {
111
102
  config.value = {...config.value, ...res};
112
103
  }
113
104
 
114
-
105
+ async function logout() {
106
+ await callAdminForthApi({
107
+ path: '/logout',
108
+ method: 'POST',
109
+ });
110
+ }
115
111
 
116
112
  const username = computed(() => {
117
113
  const usernameField = config.value.usernameField;
118
- return adminUser.value && adminUser.value[usernameField];
114
+ return user.value && user.value[usernameField];
119
115
  });
120
116
 
121
117
  const userFullname = computed(() => {
122
118
  const userFullnameField = config.value.userFullnameField;
123
- return adminUser.value && adminUser.value[userFullnameField];
119
+ return user.value && user.value[userFullnameField];
124
120
  })
125
121
 
126
122
 
@@ -134,11 +130,9 @@ export const useCoreStore = defineStore('core', () => {
134
130
  fetchMenuAndResource,
135
131
  fetchRecord,
136
132
  record,
137
- fetchResourceFull,
133
+ resourceColumns,
134
+ fetchColumns,
138
135
  resourceColumnsError,
139
- resourceOptions,
140
- resource,
141
- adminUser,
142
- resourceColumnsWithFilters
136
+ logout
143
137
  }
144
138
  })
@@ -1,12 +1,6 @@
1
1
  import { ref } from 'vue'
2
2
  import { defineStore } from 'pinia'
3
-
4
- type ModalContentType = {
5
- title?: string;
6
- content?: string;
7
- acceptText?: string;
8
- cancelText?: string;
9
- }
3
+ import { callAdminForthApi } from '@/utils';
10
4
 
11
5
 
12
6
  export const useModalStore = defineStore('modal', () => {
@@ -18,17 +12,13 @@ export const useModalStore = defineStore('modal', () => {
18
12
  });
19
13
  const isOpened = ref(false);
20
14
  const onAcceptFunction: any = ref(()=>{});
21
- const onCancelFunction: any = ref(()=>{});
22
15
  function togleModal() {
23
16
  isOpened.value = !isOpened.value;
24
17
  }
25
18
  function setOnAcceptFunction(func: Function) {
26
19
  onAcceptFunction.value = func;
27
20
  }
28
- function setOnCancelFunction(func: Function) {
29
- onCancelFunction.value = func;
30
- }
31
- function setModalContent(content: ModalContentType) {
21
+ function setModalContent(content: string) {
32
22
  modalContent.value = content;
33
23
  }
34
24
  function resetmodalState() {
@@ -43,6 +33,6 @@ export const useModalStore = defineStore('modal', () => {
43
33
 
44
34
  }
45
35
 
46
- return {isOpened, setModalContent,onCancelFunction, togleModal,modalContent, setOnAcceptFunction, onAcceptFunction,resetmodalState,setOnCancelFunction}
36
+ return {isOpened, setModalContent, togleModal,modalContent, setOnAcceptFunction, onAcceptFunction,resetmodalState}
47
37
 
48
38
  })
@@ -1,15 +1,8 @@
1
1
  import { onMounted, ref, resolveComponent } from 'vue';
2
- import type { CoreConfig } from './spa_types/core';
3
2
 
4
3
  import router from "./router";
5
- import { useRouter } from 'vue-router';
6
- import { useCoreStore } from './stores/core';
7
- import { useUserStore } from './stores/user';
8
4
 
9
- export async function callApi({path, method, body=undefined}: {
10
- path: string, method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'
11
- body?: any
12
- }): Promise<any> {
5
+ export async function callApi({path, method, body=undefined} ) {
13
6
  const options = {
14
7
  method,
15
8
  headers: {
@@ -19,19 +12,15 @@ export async function callApi({path, method, body=undefined}: {
19
12
  };
20
13
  const fullPath = `${import.meta.env.VITE_ADMINFORTH_PUBLIC_PATH || ''}${path}`;
21
14
  const r = await fetch(fullPath, options);
22
- if (r.status == 401 ) {
23
- useUserStore().unauthorize();
24
- router.push({ name: 'login' });
15
+ if (r.status == 401) {
16
+ console.log('router', router);
17
+ router.push({name: 'login'});
25
18
  return null;
26
- }
19
+ }
27
20
  return await r.json();
28
21
  }
29
22
 
30
- export async function callAdminForthApi({ path, method, body=undefined }: {
31
- path: string,
32
- method: 'GET' | 'POST' | 'PUT' | 'DELETE',
33
- body?: any
34
- }) {
23
+ export async function callAdminForthApi({ path, method, body=undefined }) {
35
24
  try {
36
25
  return callApi({path: `/adminapi/v1${path}`, method, body} );
37
26
  } catch (e) {
@@ -40,10 +29,9 @@ export async function callAdminForthApi({ path, method, body=undefined }: {
40
29
  }
41
30
  }
42
31
 
43
- export function getCustomComponent({ file, meta }: { file: string, meta: any }) {
44
- const name = file.replace(/@/g, '').replace(/\./g, '').replace(/\//g, '');
45
- return resolveComponent(name);
46
- }
32
+
33
+
34
+
47
35
 
48
36
  export function getIcon(icon: string) {
49
37
  // icon format is "feather:icon-name". We need to get IconName in pascal case
@@ -53,51 +41,4 @@ export function getIcon(icon: string) {
53
41
  const [iconSet, iconName] = icon.split(':');
54
42
  const compName = 'Icon' + iconName.split('-').map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join('');
55
43
  return resolveComponent(compName);
56
- }
57
-
58
- export const loadFile = (file: string) => {
59
- if (file.startsWith('http')) {
60
- return file;
61
- }
62
- let path;
63
- let baseUrl = '';
64
- if (file.startsWith('@/')) {
65
- path = file.replace('@/', '');
66
- baseUrl = new URL(`./${path}`, import.meta.url).href;
67
- } else if (file.startsWith('@@/')) {
68
- path = file.replace('@@/', '');
69
- baseUrl = new URL(`./custom/${path}`, import.meta.url).href;
70
- } else {
71
- baseUrl = new URL(`./${file}`, import.meta.url).href;
72
- }
73
- return baseUrl;
74
- }
75
-
76
- export function checkEmptyValues(value: any, viewType: 'show' | 'list' ) {
77
- const config: CoreConfig | {} = useCoreStore().config;
78
- let emptyFieldPlaceholder = '';
79
- if (config.emptyFieldPlaceholder) {
80
- if(typeof config.emptyFieldPlaceholder === 'string') {
81
- emptyFieldPlaceholder = config.emptyFieldPlaceholder;
82
- } else {
83
- emptyFieldPlaceholder = config.emptyFieldPlaceholder?.[viewType] || '';
84
- }
85
- if (value === null || value === undefined || value === '') {
86
- return emptyFieldPlaceholder;
87
- }
88
- }
89
- return value;
90
- }
91
-
92
- export function checkAcessByAllowedActions(allowedActions:any, action:any ) {
93
- if (!allowedActions) {
94
- console.warn('allowedActions not set');
95
- return;
96
- }
97
- if(allowedActions[action] === false) {
98
- console.warn(`Action ${action} is not allowed`);
99
- router.back();
100
- }
101
- }
102
-
103
-
44
+ }
@@ -1,25 +1,16 @@
1
1
  <template>
2
2
  <div class="relative">
3
3
 
4
- <component
5
- v-for="c in coreStore?.resourceOptions?.pageInjections?.create?.beforeBreadcrumbs || []"
6
- :is="getCustomComponent(c)"
7
- :meta="c.meta"
8
- :record="coreStore.record"
9
- :resource="coreStore.resource"
10
- :adminUser="coreStore.adminUser"
11
- />
12
-
13
4
  <BreadcrumbsWithButtons>
14
5
  <!-- save and cancle -->
15
6
  <button @click="$router.back()"
16
- class="flex items-center py-1 px-3 me-2 text-sm font-medium rounded-default text-gray-900 focus:outline-none bg-white rounded border border-gray-300 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700"
7
+ class="flex items-center py-1 px-3 me-2 mb-2 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded border border-gray-300 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700"
17
8
  >
18
9
  Cancel
19
10
  </button>
20
11
 
21
12
  <button @click="saveRecord"
22
- class="flex items-center py-1 px-3 text-sm font-medium rounded-default text-red-600 focus:outline-none bg-white rounded border border-gray-300 hover:bg-gray-100 hover:text-red-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-red-500 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700 disabled:opacity-50"
13
+ class="flex items-center py-1 px-3 mb-2 text-sm font-medium text-red-600 focus:outline-none bg-white rounded border border-gray-300 hover:bg-gray-100 hover:text-red-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-red-500 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700 disabled:opacity-50"
23
14
  :disabled="saving || (validating && !isValid)"
24
15
  >
25
16
  <svg v-if="saving" x
@@ -31,55 +22,32 @@
31
22
 
32
23
  </BreadcrumbsWithButtons>
33
24
 
34
- <component
35
- v-for="c in coreStore?.resourceOptions?.pageInjections?.create?.afterBreadcrumbs || []"
36
- :is="getCustomComponent(c)"
37
- :meta="c.meta"
38
- :record="coreStore.record"
39
- :resource="coreStore.resource"
40
- :adminUser="coreStore.adminUser"
41
- />
42
-
43
25
  <SingleSkeletLoader v-if="loading"></SingleSkeletLoader>
44
-
45
26
 
46
27
  <ResourceForm
47
28
  v-else
48
- :record="record"
49
- :resource="coreStore.resource"
29
+ :record="{}"
30
+ :resourceColumns="coreStore.resourceColumns"
50
31
  @update:record="onUpdateRecord"
51
32
  @update:isValid="isValid = $event"
52
33
  :validating="validating"
53
- :source="'create'"
54
34
  >
55
35
  </ResourceForm>
56
36
 
57
- <component
58
- v-for="c in coreStore?.resourceOptions?.pageInjections?.create?.bottom || []"
59
- :is="getCustomComponent(c)"
60
- :meta="c.meta"
61
- :record="coreStore.record"
62
- :resource="coreStore.resource"
63
- :adminUser="coreStore.adminUser"
64
- />
65
-
66
37
  </div>
67
38
  </template>
68
39
 
69
40
 
70
41
  <script setup>
71
42
 
72
- import BreadcrumbsWithButtons from '@/components/BreadcrumbsWithButtons.vue';
73
- import ResourceForm from '@/components/ResourceForm.vue';
74
- import SingleSkeletLoader from '@/components/SingleSkeletLoader.vue';
43
+ import { ref, computed, onMounted } from 'vue';
44
+ import { useRoute, useRouter } from 'vue-router';
75
45
  import { useCoreStore } from '@/stores/core';
76
- import { callAdminForthApi, getCustomComponent,checkAcessByAllowedActions } from '@/utils';
46
+ import { callAdminForthApi } from '@/utils';
47
+ import ResourceForm from '@/components/ResourceForm.vue';
48
+ import BreadcrumbsWithButtons from '@/components/BreadcrumbsWithButtons.vue';
77
49
  import { IconFloppyDiskSolid } from '@iconify-prerendered/vue-flowbite';
78
- import { onMounted, ref, watch } from 'vue';
79
- import { useRoute, useRouter } from 'vue-router';
80
- import { computed } from 'vue';
81
- import { showErrorTost } from '@/composables/useFrontendApi';
82
-
50
+ import SingleSkeletLoader from '@/components/SingleSkeletLoader.vue';
83
51
 
84
52
  const isValid = ref(false);
85
53
  const validating = ref(false);
@@ -94,15 +62,6 @@ const record = ref({});
94
62
 
95
63
  const coreStore = useCoreStore();
96
64
 
97
- const initalValues = computed(() => {
98
- if (!route.query.values) {
99
- return {};
100
- }
101
- return JSON.parse(decodeURIComponent(route.query.values));
102
- });
103
-
104
-
105
-
106
65
  async function onUpdateRecord(newRecord) {
107
66
  console.log('newRecord', newRecord);
108
67
  record.value = newRecord;
@@ -110,16 +69,13 @@ async function onUpdateRecord(newRecord) {
110
69
 
111
70
  onMounted(async () => {
112
71
  loading.value = true;
113
- record.value = initalValues.value;
114
- await coreStore.fetchResourceFull({
72
+ coreStore.fetchColumns({
115
73
  resourceId: route.params.resourceId
116
74
  });
117
75
  loading.value = false;
118
- checkAcessByAllowedActions(coreStore.resourceOptions.allowedActions,'create');
119
76
  });
120
77
 
121
78
  async function saveRecord() {
122
- console.log('saveRecord isValid', isValid.value);
123
79
  if (!isValid.value) {
124
80
  validating.value = true;
125
81
  return;
@@ -135,21 +91,12 @@ async function saveRecord() {
135
91
  record: record.value,
136
92
  },
137
93
  });
138
- if (response.error) {
139
- showErrorTost(response.error);
140
- }
141
94
  saving.value = false;
142
- if (route.query.returnTo) {
143
- router.push(route.query.returnTo);
144
- } else {
145
- router.push({
146
- name: 'resource-show',
147
- params: {
148
- resourceId: route.params.resourceId,
149
- primaryKey: response.newRecordId
150
- }
151
- });
152
- }
95
+ router.push({ name: 'resource-show', params: {
96
+ resourceId: route.params.resourceId,
97
+ primaryKey: response.newRecordId
98
+
99
+ } });
153
100
  }
154
101
 
155
102
 
@@ -1,24 +1,16 @@
1
1
  <template>
2
2
  <div class="relative">
3
- <component
4
- v-for="c in coreStore?.resourceOptions?.pageInjections?.edit?.beforeBreadcrumbs || []"
5
- :is="getCustomComponent(c)"
6
- :meta="c.meta"
7
- :record="editableRecord"
8
- :resource="coreStore.resource"
9
- :adminUser="coreStore.adminUser"
10
- />
11
3
 
12
4
  <BreadcrumbsWithButtons>
13
5
  <!-- save and cancle -->
14
6
  <button @click="$router.back()"
15
- class="flex items-center py-1 px-3 me-2 text-sm font-medium text-gray-900 rounded-default focus:outline-none bg-white rounded border border-gray-300 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700"
7
+ class="flex items-center py-1 px-3 me-2 mb-2 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded border border-gray-300 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700"
16
8
  >
17
9
  Cancel
18
10
  </button>
19
11
 
20
12
  <button @click="saveRecord"
21
- class="flex items-center py-1 px-3 text-sm font-medium rounded-default text-red-600 focus:outline-none bg-white rounded border border-gray-300 hover:bg-gray-100 hover:text-red-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-red-500 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700"
13
+ class="flex items-center py-1 px-3 mb-2 text-sm font-medium text-red-600 focus:outline-none bg-white rounded border border-gray-300 hover:bg-gray-100 hover:text-red-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-red-500 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700"
22
14
  :disabled="saving || (validating && !isValid)"
23
15
  >
24
16
  <IconFloppyDiskSolid class="w-4 h-4" />
@@ -27,53 +19,31 @@
27
19
 
28
20
  </BreadcrumbsWithButtons>
29
21
 
30
- <component
31
- v-for="c in coreStore?.resourceOptions?.pageInjections?.edit?.afterBreadcrumbs || []"
32
- :is="getCustomComponent(c)"
33
- :meta="c.meta"
34
- :record="coreStore.record"
35
- :resource="coreStore.resource"
36
- :adminUser="coreStore.adminUser"
37
- />
38
-
39
22
  <SingleSkeletLoader v-if="loading"></SingleSkeletLoader>
40
23
 
41
24
  <ResourceForm
42
25
  v-else
43
- :record="editableRecord"
44
- :resource="coreStore.resource"
45
- :adminUser="coreStore.adminUser"
26
+ :record="coreStore.record"
27
+ :resourceColumns="coreStore.resourceColumns"
46
28
  @update:record="onUpdateRecord"
47
29
  @update:isValid="isValid = $event"
48
30
  :validating="validating"
49
- :source="'edit'"
50
31
  >
51
32
  </ResourceForm>
52
-
53
- <component
54
- v-for="c in coreStore?.resourceOptions?.pageInjections?.edit?.bottom || []"
55
- :is="getCustomComponent(c)"
56
- :meta="c.meta"
57
- :record="coreStore.record"
58
- :resource="coreStore.resource"
59
- :adminUser="coreStore.adminUser"
60
- />
61
-
62
33
  </div>
63
34
  </template>
64
35
 
65
36
 
66
37
  <script setup>
67
38
 
68
- import BreadcrumbsWithButtons from '@/components/BreadcrumbsWithButtons.vue';
69
- import ResourceForm from '@/components/ResourceForm.vue';
70
- import SingleSkeletLoader from '@/components/SingleSkeletLoader.vue';
39
+ import { ref, computed, onMounted } from 'vue';
40
+ import { useRoute, useRouter } from 'vue-router';
71
41
  import { useCoreStore } from '@/stores/core';
72
- import { callAdminForthApi, getCustomComponent,checkAcessByAllowedActions } from '@/utils';
42
+ import ResourceForm from '@/components/ResourceForm.vue';
43
+ import BreadcrumbsWithButtons from '@/components/BreadcrumbsWithButtons.vue';
73
44
  import { IconFloppyDiskSolid } from '@iconify-prerendered/vue-flowbite';
74
- import { computed, onMounted, ref } from 'vue';
75
- import { useRoute, useRouter } from 'vue-router';
76
- import { showErrorTost } from '@/composables/useFrontendApi';
45
+ import SingleSkeletLoader from '@/components/SingleSkeletLoader.vue';
46
+ import { callAdminForthApi } from '@/utils';
77
47
 
78
48
  const coreStore = useCoreStore();
79
49
 
@@ -93,31 +63,18 @@ async function onUpdateRecord(newRecord) {
93
63
  record.value = newRecord;
94
64
  }
95
65
 
96
- const editableRecord = computed(() => {
97
- const newRecord = { ...coreStore.record };
98
- if (!coreStore.resource) {
99
- return {};
100
- }
101
- coreStore.resource.columns.forEach(column => {
102
- if (column.foreignResource) {
103
- newRecord[column.name] = newRecord[column.name]?.pk
104
- }
105
- });
106
- return newRecord;
107
- })
108
66
 
109
67
  onMounted(async () => {
110
-
111
68
  loading.value = true;
112
69
 
113
- await coreStore.fetchResourceFull({
70
+ await coreStore.fetchColumns({
114
71
  resourceId: route.params.resourceId
115
72
  });
116
73
  await coreStore.fetchRecord({
117
74
  resourceId: route.params.resourceId,
118
75
  primaryKey: route.params.primaryKey,
119
76
  });
120
- checkAcessByAllowedActions(coreStore.resourceOptions.allowedActions,'edit');
77
+
121
78
  loading.value = false;
122
79
  });
123
80
 
@@ -130,26 +87,15 @@ async function saveRecord() {
130
87
  }
131
88
 
132
89
  saving.value = true;
133
-
134
- const updates = {};
135
- for (const key in record.value) {
136
- if (record.value[key] !== coreStore.record[key]) {
137
- updates[key] = record.value[key];
138
- }
139
- }
140
-
141
- const resp = await callAdminForthApi({
90
+ await callAdminForthApi({
142
91
  method: 'POST',
143
92
  path: `/update_record`,
144
93
  body: {
145
94
  resourceId: route.params.resourceId,
146
95
  recordId: route.params.primaryKey,
147
- record: updates,
96
+ record: record.value,
148
97
  },
149
98
  });
150
- if (resp.error) {
151
- showErrorTost(resp.error);
152
- }
153
99
  saving.value = false;
154
100
  router.push({ name: 'resource-show', params: { resourceId: route.params.resourceId, primaryKey: coreStore.record[coreStore.primaryKey] } });
155
101
  }
@@ -0,0 +1,8 @@
1
+ <script setup lang="ts">
2
+ </script>
3
+
4
+ <template>
5
+ <main>
6
+ 123
7
+ </main>
8
+ </template>