adminforth 1.2.95 → 1.2.98

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/index.js +2 -1
  2. package/dist/modules/restApi.js +27 -29
  3. package/dist/spa/index.html +4 -4
  4. package/dist/spa/package-lock.json +421 -6
  5. package/dist/spa/package.json +10 -2
  6. package/dist/spa/src/App.vue +236 -94
  7. package/dist/spa/src/assets/base.css +2 -0
  8. package/dist/spa/src/assets/logo.svg +19 -1
  9. package/dist/spa/src/components/AcceptModal.vue +3 -10
  10. package/dist/spa/src/components/Breadcrumbs.vue +3 -2
  11. package/dist/spa/src/components/BreadcrumbsWithButtons.vue +1 -1
  12. package/dist/spa/src/components/CustomDatePicker.vue +25 -10
  13. package/dist/spa/src/components/CustomDateRangePicker.vue +14 -5
  14. package/dist/spa/src/components/Dropdown.vue +19 -5
  15. package/dist/spa/src/components/Filters.vue +103 -38
  16. package/dist/spa/src/components/MenuLink.vue +3 -3
  17. package/dist/spa/src/components/ResourceForm.vue +205 -116
  18. package/dist/spa/{spa/src → src}/components/ResourceListTable.vue +6 -3
  19. package/dist/spa/{spa/src → src}/components/Toast.vue +9 -3
  20. package/dist/spa/src/components/ValueRenderer.vue +30 -8
  21. package/dist/spa/src/index.scss +2 -1
  22. package/dist/spa/src/main.ts +2 -2
  23. package/dist/spa/src/router/index.ts +29 -19
  24. package/dist/spa/src/stores/core.ts +65 -59
  25. package/dist/spa/src/stores/modal.ts +13 -3
  26. package/dist/spa/src/types/AdminForthConfig.ts +1477 -0
  27. package/dist/spa/src/types/FrontendAPI.ts +121 -0
  28. package/dist/spa/src/utils.ts +69 -10
  29. package/dist/spa/src/views/CreateView.vue +69 -16
  30. package/dist/spa/src/views/EditView.vue +68 -14
  31. package/dist/spa/src/views/ListView.vue +100 -371
  32. package/dist/spa/src/views/LoginView.vue +67 -29
  33. package/dist/spa/src/views/ResourceParent.vue +1 -2
  34. package/dist/spa/src/views/ShowView.vue +116 -25
  35. package/dist/spa/tailwind.config.js +8 -3
  36. package/dist/spa/vite.config.ts +13 -0
  37. package/index.ts +2 -1
  38. package/modules/restApi.ts +31 -31
  39. package/package.json +1 -1
  40. package/dist/spa/.vscode/extensions.json +0 -6
  41. package/dist/spa/public/favicon.ico +0 -0
  42. package/dist/spa/spa/.eslintrc.cjs +0 -14
  43. package/dist/spa/spa/.vscode/extensions.json +0 -6
  44. package/dist/spa/spa/README.md +0 -39
  45. package/dist/spa/spa/env.d.ts +0 -1
  46. package/dist/spa/spa/index.html +0 -23
  47. package/dist/spa/spa/package-lock.json +0 -4573
  48. package/dist/spa/spa/package.json +0 -49
  49. package/dist/spa/spa/postcss.config.js +0 -6
  50. package/dist/spa/spa/public/favicon.ico +0 -0
  51. package/dist/spa/spa/src/App.vue +0 -314
  52. package/dist/spa/spa/src/assets/base.css +0 -0
  53. package/dist/spa/spa/src/assets/logo.svg +0 -19
  54. package/dist/spa/spa/src/components/AcceptModal.vue +0 -45
  55. package/dist/spa/spa/src/components/Breadcrumbs.vue +0 -40
  56. package/dist/spa/spa/src/components/BreadcrumbsWithButtons.vue +0 -26
  57. package/dist/spa/spa/src/components/CustomDatePicker.vue +0 -174
  58. package/dist/spa/spa/src/components/CustomDateRangePicker.vue +0 -218
  59. package/dist/spa/spa/src/components/Dropdown.vue +0 -168
  60. package/dist/spa/spa/src/components/Filters.vue +0 -222
  61. package/dist/spa/spa/src/components/HelloWorld.vue +0 -17
  62. package/dist/spa/spa/src/components/MenuLink.vue +0 -24
  63. package/dist/spa/spa/src/components/ResourceForm.vue +0 -289
  64. package/dist/spa/spa/src/components/SingleSkeletLoader.vue +0 -13
  65. package/dist/spa/spa/src/components/ValueRenderer.vue +0 -66
  66. package/dist/spa/spa/src/components/icons/IconCalendar.vue +0 -5
  67. package/dist/spa/spa/src/components/icons/IconCommunity.vue +0 -7
  68. package/dist/spa/spa/src/components/icons/IconDocumentation.vue +0 -7
  69. package/dist/spa/spa/src/components/icons/IconEcosystem.vue +0 -7
  70. package/dist/spa/spa/src/components/icons/IconSupport.vue +0 -7
  71. package/dist/spa/spa/src/components/icons/IconTime.vue +0 -5
  72. package/dist/spa/spa/src/components/icons/IconTooling.vue +0 -19
  73. package/dist/spa/spa/src/index.scss +0 -27
  74. package/dist/spa/spa/src/main.ts +0 -18
  75. package/dist/spa/spa/src/router/index.ts +0 -63
  76. package/dist/spa/spa/src/stores/core.ts +0 -144
  77. package/dist/spa/spa/src/stores/modal.ts +0 -48
  78. package/dist/spa/spa/src/utils.ts +0 -103
  79. package/dist/spa/spa/src/views/CreateView.vue +0 -156
  80. package/dist/spa/spa/src/views/EditView.vue +0 -157
  81. package/dist/spa/spa/src/views/HomeView.vue +0 -8
  82. package/dist/spa/spa/src/views/ListView.vue +0 -258
  83. package/dist/spa/spa/src/views/LoginView.vue +0 -143
  84. package/dist/spa/spa/src/views/ResourceParent.vue +0 -17
  85. package/dist/spa/spa/src/views/ShowView.vue +0 -184
  86. package/dist/spa/spa/tailwind.config.js +0 -17
  87. package/dist/spa/spa/tsconfig.app.json +0 -14
  88. package/dist/spa/spa/tsconfig.json +0 -11
  89. package/dist/spa/spa/tsconfig.node.json +0 -19
  90. package/dist/spa/spa/vite.config.ts +0 -56
  91. package/dist/spa/src/views/HomeView.vue +0 -8
  92. package/dist/types.js +0 -30
  93. /package/dist/spa/{spa/public → public}/assets/favicon.png +0 -0
  94. /package/dist/spa/{spa/src → src}/components/CustomRangePicker.vue +0 -0
  95. /package/dist/spa/{spa/src → src}/components/SkeleteLoader.vue +0 -0
  96. /package/dist/spa/{spa/src → src}/composables/useFrontendApi.ts +0 -0
  97. /package/dist/spa/{spa/src → src}/composables/useStores.ts +0 -0
  98. /package/dist/spa/{spa/src → src}/spa_types/core.ts +0 -0
  99. /package/dist/spa/{spa/src → src}/stores/filters.ts +0 -0
  100. /package/dist/spa/{spa/src → src}/stores/toast.ts +0 -0
  101. /package/dist/spa/{spa/src → src}/stores/user.ts +0 -0
@@ -1,17 +1,18 @@
1
1
  <template>
2
2
  <div class="relative flex items-center justify-center min-h-screen bg-gray-100 dark:bg-gray-800"
3
3
  :style="coreStore.config?.loginBackgroundImage ? {
4
- 'background-image': 'url(' + coreStore.config?.loginBackgroundImage + ')',
4
+ 'background-image': 'url(' + loadFile(coreStore.config?.loginBackgroundImage) + ')',
5
5
  'background-size': 'cover',
6
6
  'background-position': 'center',
7
7
  'background-blend-mode': 'darken'
8
8
  }: {}"
9
9
  >
10
- <!-- Main modal -->
10
+
11
+ <!-- Main modal -->
11
12
  <div id="authentication-modal" tabindex="-1" class=" overflow-y-auto overflow-x-hidden z-50 min-w-[400px] justify-center items-center md:inset-0 h-[calc(100%-1rem)] max-h-full">
12
13
  <div class="relative p-4 w-full max-w-md max-h-full">
13
14
  <!-- Modal content -->
14
- <div class="relative bg-white rounded-lg shadow dark:bg-gray-700">
15
+ <div class="relative bg-white rounded-lg shadow dark:bg-gray-700 dark:shadow-black" >
15
16
  <!-- Modal header -->
16
17
  <div class="flex items-center justify-between p-4 md:p-5 border-b rounded-t dark:border-gray-600">
17
18
  <h3 class="text-xl font-semibold text-gray-900 dark:text-white">
@@ -21,17 +22,23 @@
21
22
  </div>
22
23
  <!-- Modal body -->
23
24
  <div class="p-4 md:p-5">
24
- <form class="space-y-4" @submit.prevent>
25
+ <form class="space-y-4" @submit.prevent>
25
26
  <div>
26
27
  <label for="username" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Your {{ coreStore.config?.usernameFieldName?.toLowerCase() }}</label>
27
- <input type="username" name="username" id="username" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white" placeholder="name@company.com" required />
28
+ <input type="username" name="username" id="username"
29
+ ref="usernameInput"
30
+ @keydown.enter="passwordInput.focus()"
31
+ class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white" placeholder="name@company.com" required />
28
32
  </div>
29
33
  <div class="relative">
30
34
  <label for="password" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Your password</label>
31
- <input :type="!showPw ? 'password': 'text'" name="password" id="password" placeholder="••••••••" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white" required />
35
+ <input
36
+ ref="passwordInput"
37
+ @keydown.enter="login"
38
+ :type="!showPw ? 'password': 'text'" name="password" id="password" placeholder="••••••••" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white" required />
32
39
  <button type="button" @click="showPw = !showPw" class="absolute top-12 right-3 -translate-y-1/2 text-gray-400 dark:text-gray-300">
33
- <IconEyeSolid class="w-5 h-5" v-if="!showPw" />
34
- <IconEyeSlashSolid class="w-5 h-5" v-else />
40
+ <IconEyeSolid class="w-5 h-5" v-if="!showPw" />
41
+ <IconEyeSlashSolid class="w-5 h-5" v-else />
35
42
  </button>
36
43
  </div>
37
44
  <!-- <div class="flex justify-between">
@@ -54,6 +61,17 @@
54
61
  </div>
55
62
  </div>
56
63
 
64
+
65
+ <div v-if="coreStore.config?.loginPromptHTML"
66
+ class="flex items-center p-4 mb-4 text-sm text-gray-800 rounded-lg bg-gray-50 dark:bg-gray-800 dark:text-gray-400" role="alert"
67
+ >
68
+ <svg class="flex-shrink-0 inline w-4 h-4 me-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
69
+ <path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM12 15H8a1 1 0 0 1 0-2h1v-3H8a1 1 0 0 1 0-2h2a1 1 0 0 1 1 1v4h1a1 1 0 0 1 0 2Z"/>
70
+ </svg>
71
+ <span class="sr-only">Info</span>
72
+ <div v-html="coreStore.config?.loginPromptHTML"></div>
73
+ </div>
74
+
57
75
  <button
58
76
  @click="login"
59
77
  type="submit" class="flex items-center justify-center gap-1 w-full text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">
@@ -78,44 +96,64 @@
78
96
 
79
97
  <script setup>
80
98
 
81
- import { onMounted, ref } from 'vue';
99
+ import { onMounted, ref, watchEffect } from 'vue';
82
100
  import { useCoreStore } from '@/stores/core';
101
+ import { useUserStore } from '@/stores/user';
83
102
  import { IconEyeSolid, IconEyeSlashSolid } from '@iconify-prerendered/vue-flowbite';
84
- import { callAdminForthApi } from '@/utils';
103
+ import { callAdminForthApi, loadFile } from '@/utils';
85
104
  import { useRouter } from 'vue-router';
86
105
 
106
+ const passwordInput = ref(null);
107
+ const usernameInput = ref(null);
108
+
87
109
  const router = useRouter();
88
110
  const inProgress = ref(false);
89
111
 
90
112
  const coreStore = useCoreStore();
113
+ const user = useUserStore();
114
+
91
115
 
92
116
  const showPw = ref(false);
93
117
 
94
118
  const error = ref(null);
95
119
 
96
- onMounted(() => {
97
- coreStore.getPublicConfig()
120
+ onMounted(async () => {
121
+ await coreStore.getPublicConfig();
122
+ if (coreStore.config?.demoCredentials) {
123
+ console.log('Setting demo credentials');
124
+ const [username, password] = coreStore.config.demoCredentials.split(':');
125
+ usernameInput.value.value = username;
126
+ passwordInput.value.value = password;
127
+ }
128
+ usernameInput.value.focus();
98
129
  });
99
130
 
131
+
100
132
  async function login() {
101
- inProgress.value = true;
102
- const resp = await callAdminForthApi({
103
- path: '/login',
104
- method: 'POST',
105
- body: {
106
- username: document.getElementById('username').value,
107
- password: document.getElementById('password').value,
108
- }
109
- });
110
- inProgress.value = false;
111
- if (resp.error) {
112
- error.value = resp.error;
113
- } else {
114
- error.value = null;
115
- router.push('/');
116
- await coreStore.fetchMenuAndResource();
133
+ const username = usernameInput.value.value;
134
+ const password = passwordInput.value.value;
135
+
136
+ if (!username || !password) {
137
+ return;
138
+ }
139
+ inProgress.value = true;
140
+ const resp = await callAdminForthApi({
141
+ path: '/login',
142
+ method: 'POST',
143
+ body: {
144
+ username,
145
+ password,
117
146
  }
118
-
147
+ });
148
+ inProgress.value = false;
149
+ if (resp.error) {
150
+ error.value = resp.error;
151
+ } else if (resp.redirectTo) {
152
+ router.push(resp.redirectTo);
153
+ } else {
154
+ error.value = null;
155
+ await user.finishLogin();
156
+ }
119
157
  }
120
158
 
121
159
 
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div>
2
+ <div :key="`${$route?.params.resourceId}---${$route?.params.primaryKey}`" class="p-4">
3
3
  <RouterView/>
4
4
  </div>
5
5
  </template>
@@ -11,7 +11,6 @@
11
11
 
12
12
 
13
13
  import { useCoreStore } from '@/stores/core';
14
- import { onMounted } from 'vue';
15
14
 
16
15
  const coreStore = useCoreStore()
17
16
 
@@ -1,21 +1,38 @@
1
1
  <template>
2
2
  <div class="relative">
3
+ <component
4
+ v-for="c in coreStore?.resourceOptions?.pageInjections?.show?.beforeBreadcrumbs || []"
5
+ :is="getCustomComponent(c)"
6
+ :meta="c.meta"
7
+ :record="coreStore.record"
8
+ :resource="coreStore.resource"
9
+ :adminUser="coreStore.adminUser"
10
+ />
3
11
  <BreadcrumbsWithButtons>
4
- <RouterLink :to="{ name: 'resource-edit', params: { resourceId: $route.params.resourceId, primaryKey: $route.params.primaryKey } }"
5
- 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"
12
+ <RouterLink v-if="coreStore?.resourceOptions?.allowedActions?.edit" :to="{ name: 'resource-edit', params: { resourceId: $route.params.resourceId, primaryKey: $route.params.primaryKey } }"
13
+ class="flex items-center py-1 px-3 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded-default 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"
6
14
  >
7
15
  <IconPenSolid class="w-4 h-4" />
8
16
  Edit
9
17
  </RouterLink>
10
18
 
11
- <button @click="deleteRecord"
12
- 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"
19
+ <button v-if="coreStore?.resourceOptions?.allowedActions?.delete" @click="deleteRecord"
20
+ class="flex items-center py-1 px-3 text-sm font-medium rounded-default text-red-600 focus:outline-none bg-white 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
21
  >
14
22
  <IconTrashBinSolid class="w-4 h-4" />
15
23
  Delete
16
24
  </button>
17
25
  </BreadcrumbsWithButtons>
18
26
 
27
+ <component
28
+ v-for="c in coreStore?.resourceOptions?.pageInjections?.show?.afterBreadcrumbs || []"
29
+ :is="getCustomComponent(c)"
30
+ :meta="c.meta"
31
+ :record="coreStore.record"
32
+ :resource="coreStore.resource"
33
+ :adminUser="coreStore.adminUser"
34
+ />
35
+
19
36
  <div v-if="loading" role="status" class="max-w-sm animate-pulse">
20
37
  <div class="h-2.5 bg-gray-200 rounded-full dark:bg-gray-700 w-48 mb-4"></div>
21
38
  <div class="h-2 bg-gray-200 rounded-full dark:bg-gray-700 max-w-[360px] mb-2.5"></div>
@@ -26,35 +43,69 @@
26
43
  <span class="sr-only">Loading...</span>
27
44
  </div>
28
45
  <div
29
- v-else
30
- class="relative overflow-x-auto shadow-md sm:rounded-lg"
46
+ v-else-if="coreStore.record"
47
+ class="relative overflow-x-auto rounded-default shadow-resourseFormShadow "
31
48
  >
32
- <table class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400">
33
- <thead class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
49
+ <table class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400 rounded-default">
50
+ <thead class="text-xs text-gray-700 uppercase bg-lightFormHeading dark:bg-gray-700 dark:text-gray-400">
34
51
  <tr>
35
52
  <th scope="col" class="px-6 py-3">
36
53
  Field
37
54
  </th>
38
- <th scope="col" class="px-6 py-3 w-4/6">
55
+ <th scope="col" class="px-6 py-3 w-5/6">
39
56
  Value
40
57
  </th>
41
58
  </tr>
42
59
  </thead>
43
60
  <tbody>
44
- <tr v-for="column in coreStore.resourceColumns?.filter(c => c.showIn.includes('show'))" :key="column.name"
45
- class="odd:bg-white odd:dark:bg-gray-900 even:bg-gray-50 even:dark:bg-gray-800 border-b dark:border-gray-700"
61
+ <tr v-for="column in coreStore.resource?.columns.filter(c => c.showIn.includes('show'))" :key="column.name"
62
+ class="bg-lightForm bg-darkForm odd:dark:bg-gray-900 even:dark:bg-gray-800 border-b dark:border-gray-700"
46
63
  >
47
- <td class="px-6 py-4 whitespace-nowrap">
48
- {{ column.label }}
64
+ <component
65
+ v-if="column.components?.showRow"
66
+ :is="getCustomComponent(column.components.showRow)"
67
+ :meta="column.components.showRow.meta"
68
+ :column="column"
69
+ :resource="coreStore.resource"
70
+ :record="coreStore.record"
71
+ />
72
+ <template v-else>
73
+ <td class="px-6 py-4 whitespace-nowrap "> <!--align-top-->
74
+ {{ column.label }}
49
75
  </td>
50
76
  <td class="px-6 py-4 whitespace-nowrap whitespace-pre-wrap">
51
- <ValueRenderer :column="column" :row="coreStore.record" />
77
+ <component
78
+ v-if="column?.components?.show"
79
+ :is="getCustomComponent(column?.components?.show)"
80
+ :resource="coreStore.resource"
81
+ :meta="column.components.show.meta"
82
+ :column="column"
83
+ :record="coreStore.record"
84
+ />
85
+ <ValueRenderer v-else
86
+ :column="column"
87
+ :record="coreStore.record"
88
+ />
52
89
  </td>
90
+ </template>
53
91
  </tr>
54
-
55
92
  </tbody>
56
- </table>
57
- </div>
93
+ </table>
94
+ </div>
95
+
96
+ <div v-else class="text-center text-gray-500 dark:text-gray-400 mt-10">
97
+ Ooops. Record not found
98
+ </div>
99
+
100
+ <component
101
+ v-for="c in coreStore?.resourceOptions?.pageInjections?.show?.bottom || []"
102
+ :is="getCustomComponent(c)"
103
+ :meta="c.meta"
104
+ :column="column"
105
+ :record="coreStore.record"
106
+ :resource="coreStore.resource"
107
+ :adminUser="coreStore.adminUser"
108
+ />
58
109
 
59
110
 
60
111
  </div>
@@ -63,31 +114,71 @@
63
114
 
64
115
  <script setup>
65
116
 
66
- import { ref, computed, onMounted } from 'vue';
67
- import { useRoute } from 'vue-router';
68
- import { callAdminForthApi } from '@/utils';
69
117
  import BreadcrumbsWithButtons from '@/components/BreadcrumbsWithButtons.vue';
70
- import { IconPenSolid, IconTrashBinSolid } from '@iconify-prerendered/vue-flowbite';
71
- import { useCoreStore } from '@/stores/core';
118
+
72
119
  import ValueRenderer from '@/components/ValueRenderer.vue';
120
+ import { useCoreStore } from '@/stores/core';
121
+ import { useModalStore } from '@/stores/modal';
122
+ import { getCustomComponent, checkAcessByAllowedActions } from '@/utils';
123
+ import { IconPenSolid, IconTrashBinSolid } from '@iconify-prerendered/vue-flowbite';
124
+ import { onMounted, ref } from 'vue';
125
+ import { useRoute,useRouter } from 'vue-router';
126
+ import {callAdminForthApi} from '@/utils';
127
+ import { showSuccesTost, showErrorTost } from '@/composables/useFrontendApi';
73
128
 
74
129
 
75
130
  const item = ref(null);
76
131
  const route = useRoute();
77
- const loading = ref(false);
132
+ const router = useRouter();
133
+ const loading = ref(true);
134
+
135
+ console.log(route.params,'showWiev');
136
+
78
137
 
79
138
  const coreStore = useCoreStore();
139
+ const modalStore = useModalStore();
80
140
 
81
141
  onMounted(async () => {
82
142
  loading.value = true;
83
- await coreStore.fetchColumns({
143
+ await coreStore.fetchResourceFull({
84
144
  resourceId: route.params.resourceId
85
145
  });
86
146
  await coreStore.fetchRecord({
87
147
  resourceId: route.params.resourceId,
88
148
  primaryKey: route.params.primaryKey,
89
149
  });
150
+ checkAcessByAllowedActions(coreStore.resourceOptions.allowedActions,'show');
90
151
  loading.value = false;
91
152
  });
92
153
 
93
- </script>
154
+ async function deleteRecord(row) {
155
+ const data = await window.adminforth.confirm({
156
+ message: 'Are you sure you want to delete this item?',
157
+ yes: 'Delete',
158
+ no: 'Cancel',
159
+ });
160
+ if (data) {
161
+ try {
162
+ const res = await callAdminForthApi({
163
+ path: '/delete_record',
164
+ method: 'POST',
165
+ body: {
166
+ resourceId: route.params.resourceId,
167
+ primaryKey: route.params.primaryKey,
168
+ }});
169
+ if (!res.error){
170
+ router.push({ name: 'resource-list', params: { resourceId: route.params.resourceId } });
171
+ showSuccesTost('Record deleted successfully')
172
+ } else {
173
+ showErrorTost(res.error)
174
+ }
175
+
176
+ } catch (e) {
177
+ console.error(e);
178
+ };
179
+ }
180
+
181
+
182
+ }
183
+
184
+ </script>
@@ -1,12 +1,17 @@
1
1
  /** @type {import('tailwindcss').Config} */
2
2
  export default {
3
- content: ["./src/**/*.{vue, js}", "./node_modules/flowbite/**/*.js"],
3
+ content: ["./src/**/*.{vue, js, ts, tsx}","./src/*.{vue, js, ts, tsx}", "./index.html", "./node_modules/flowbite/**/*.js"],
4
4
  theme: {
5
- extend: {},
5
+ extend: {
6
+ /* IMPORTANT:ADMINFORTH TAILWIND STYLES */
7
+ }
6
8
  },
9
+
7
10
  darkMode: 'class',
8
11
  plugins: [
9
- require('flowbite/plugin'),
12
+ require('flowbite/plugin')({
13
+ charts: true,
14
+ }),
10
15
  ],
11
16
  }
12
17
 
@@ -2,6 +2,7 @@ import { fileURLToPath, URL } from 'node:url'
2
2
 
3
3
  import { defineConfig } from 'vite'
4
4
  import vue from '@vitejs/plugin-vue'
5
+ import { clear, error } from 'node:console';
5
6
 
6
7
 
7
8
  const customLogger = {
@@ -11,6 +12,12 @@ const customLogger = {
11
12
  console.log(msg);
12
13
  }
13
14
  },
15
+ warnOnce(msg: string) {
16
+ console.warn('warn once', msg);
17
+ if (!this.hasWarned) {
18
+ this.hasWarned = true;
19
+ }
20
+ },
14
21
  warn(msg: string) {
15
22
  console.warn(msg);
16
23
  },
@@ -20,7 +27,13 @@ const customLogger = {
20
27
  clear() {
21
28
  console.clear();
22
29
  },
30
+ clearScreen() {
31
+ console.clear();
32
+ },
23
33
  hasWarned: false,
34
+ hasErrorLogged: (error: any): boolean => {
35
+ return false;
36
+ }
24
37
  };
25
38
 
26
39
  // https://vitejs.dev/config/
package/index.ts CHANGED
@@ -18,11 +18,12 @@ import {
18
18
  } from './types/AdminForthConfig.js';
19
19
  import AdminForthPlugin from './basePlugin.js';
20
20
  import ConfigValidator from './modules/configValidator.js';
21
- import AdminForthRestAPI from './modules/restApi.js';
21
+ import AdminForthRestAPI, { interpretResource } from './modules/restApi.js';
22
22
  import ClickhouseConnector from './dataConnectors/clickhouse.js';
23
23
 
24
24
  // exports
25
25
  export * from './types/AdminForthConfig.js';
26
+ export { interpretResource };
26
27
  export { AdminForthPlugin };
27
28
 
28
29
 
@@ -21,6 +21,32 @@ import { ADMINFORTH_VERSION, listify } from './utils.js';
21
21
 
22
22
  import AdminForthAuth from "../auth.js";
23
23
 
24
+
25
+ export async function interpretResource(adminUser: AdminUser, resource: AdminForthResource, meta: any, source: ActionCheckSource): Promise<{allowedActions: AllowedActionsResolved}> {
26
+ // if (process.env.HEAVY_DEBUG) {
27
+ // console.log('🪲Interpreting resource', resource.resourceId, source, 'adminUser', adminUser);
28
+ // }
29
+ const allowedActions = {};
30
+
31
+ await Promise.all(
32
+ Object.entries(resource.options?.allowedActions || {}).map(
33
+ async ([key, value]: [string, AllowedActionValue]) => {
34
+ if (process.env.HEAVY_DEBUG) {
35
+ console.log('🪲checking for allowed call', key, 'value:', value, 'typeof', typeof value);
36
+ }
37
+
38
+ // if callable then call
39
+ if (typeof value === 'function') {
40
+ allowedActions[key] = await value({ adminUser, resource, meta, source });
41
+ } else {
42
+ allowedActions[key] = value;
43
+ }
44
+ })
45
+ );
46
+
47
+ return { allowedActions };
48
+ }
49
+
24
50
  export default class AdminForthRestAPI {
25
51
 
26
52
  adminforth: IAdminForth;
@@ -188,13 +214,10 @@ export default class AdminForthRestAPI {
188
214
  }
189
215
 
190
216
  async function processMenuItem(menuItem) {
191
- console.log('super hueta')
192
-
193
217
  if (menuItem.badge) {
194
- console.log('hueta')
195
- // if (typeof menuItem.badge !== 'string') {
218
+ if (typeof menuItem.badge === 'function') {
196
219
  menuItem.badge = await menuItem.badge(adminUser);
197
- // }
220
+ }
198
221
  }
199
222
  }
200
223
  let newMenu = []
@@ -214,12 +237,12 @@ export default class AdminForthRestAPI {
214
237
  continue
215
238
  }
216
239
  }
217
- processMenuItem(newChild)
240
+ await processMenuItem(newChild)
218
241
  newChildren.push(newChild)
219
242
  }
220
243
  newMenuItem = {...newMenuItem, children: newChildren}
221
244
  }
222
- processMenuItem(newMenuItem)
245
+ await processMenuItem(newMenuItem)
223
246
  newMenu.push(newMenuItem)
224
247
  }
225
248
 
@@ -249,30 +272,7 @@ export default class AdminForthRestAPI {
249
272
  },
250
273
  });
251
274
 
252
- async function interpretResource(adminUser: AdminUser, resource: AdminForthResource, meta: any, source: ActionCheckSource): Promise<{allowedActions: AllowedActionsResolved}> {
253
- // if (process.env.HEAVY_DEBUG) {
254
- // console.log('🪲Interpreting resource', resource.resourceId, source, 'adminUser', adminUser);
255
- // }
256
- const allowedActions = {};
257
-
258
- await Promise.all(
259
- Object.entries(resource.options?.allowedActions || {}).map(
260
- async ([key, value]: [string, AllowedActionValue]) => {
261
- if (process.env.HEAVY_DEBUG) {
262
- console.log('🪲checking for allowed call', key, 'value:', value, 'typeof', typeof value);
263
- }
264
-
265
- // if callable then call
266
- if (typeof value === 'function') {
267
- allowedActions[key] = await value({ adminUser, resource, meta, source });
268
- } else {
269
- allowedActions[key] = value;
270
- }
271
- })
272
- );
273
-
274
- return { allowedActions };
275
- }
275
+
276
276
 
277
277
  function checkAccess(action: AllowedActionsEnum, allowedActions: AllowedActions): { allowed: boolean, error?: string } {
278
278
  const allowed = (allowedActions[action] as boolean | string | undefined);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adminforth",
3
- "version": "1.2.95",
3
+ "version": "1.2.98",
4
4
  "description": "OpenSource Vue3 powered forth-generation admin panel",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -1,6 +0,0 @@
1
- {
2
- "recommendations": [
3
- "Vue.volar",
4
- "dbaeumer.vscode-eslint"
5
- ]
6
- }
Binary file
@@ -1,14 +0,0 @@
1
- /* eslint-env node */
2
- require('@rushstack/eslint-patch/modern-module-resolution')
3
-
4
- module.exports = {
5
- root: true,
6
- 'extends': [
7
- 'plugin:vue/vue3-essential',
8
- 'eslint:recommended',
9
- '@vue/eslint-config-typescript'
10
- ],
11
- parserOptions: {
12
- ecmaVersion: 'latest'
13
- }
14
- }
@@ -1,6 +0,0 @@
1
- {
2
- "recommendations": [
3
- "Vue.volar",
4
- "dbaeumer.vscode-eslint"
5
- ]
6
- }
@@ -1,39 +0,0 @@
1
- # spa
2
-
3
- This template should help get you started developing with Vue 3 in Vite.
4
-
5
- ## Recommended IDE Setup
6
-
7
- [VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur).
8
-
9
- ## Type Support for `.vue` Imports in TS
10
-
11
- TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) to make the TypeScript language service aware of `.vue` types.
12
-
13
- ## Customize configuration
14
-
15
- See [Vite Configuration Reference](https://vitejs.dev/config/).
16
-
17
- ## Project Setup
18
-
19
- ```sh
20
- npm install
21
- ```
22
-
23
- ### Compile and Hot-Reload for Development
24
-
25
- ```sh
26
- npm run dev
27
- ```
28
-
29
- ### Type-Check, Compile and Minify for Production
30
-
31
- ```sh
32
- npm run build
33
- ```
34
-
35
- ### Lint with [ESLint](https://eslint.org/)
36
-
37
- ```sh
38
- npm run lint
39
- ```
@@ -1 +0,0 @@
1
- /// <reference types="vite/client" />
@@ -1,23 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <link rel="icon" href="/* IMPORTANT:ADMINFORTH FAVICON */">
6
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
- <title>/* IMPORTANT:ADMINFORTH TITLE */</title>
8
- <!--
9
- <script>
10
- // On page load or when changing themes, best to add inline in `head` to avoid FOUC
11
- if (localStorage.getItem('color-theme') === 'dark' || (!('color-theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
12
- document.documentElement.classList.add('dark');
13
- } else {
14
- document.documentElement.classList.remove('dark')
15
- }
16
- </script> -->
17
-
18
- </head>
19
- <body class=" ">
20
- <div id="app" class="min-h-screen bg-lightHtml dark:bg-darkHtml"></div>
21
- <script type="module" src="/src/main.ts"></script>
22
- </body>
23
- </html>