adminforth 1.2.56 → 1.2.58

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 (34) hide show
  1. package/dist/spa/index.html +1 -1
  2. package/dist/spa/package-lock.json +215 -1
  3. package/dist/spa/package.json +2 -0
  4. package/dist/spa/spa/src/types/AdminForthConfig.ts +1477 -0
  5. package/dist/spa/spa/src/types/FrontendAPI.ts +121 -0
  6. package/dist/spa/src/App.vue +176 -133
  7. package/dist/spa/src/assets/base.css +2 -0
  8. package/dist/spa/src/components/Breadcrumbs.vue +3 -2
  9. package/dist/spa/src/components/CustomDatePicker.vue +6 -4
  10. package/dist/spa/src/components/CustomRangePicker.vue +11 -7
  11. package/dist/spa/src/components/Filters.vue +16 -5
  12. package/dist/spa/src/components/MenuLink.vue +3 -3
  13. package/dist/spa/src/components/ResourceForm.vue +140 -111
  14. package/dist/spa/src/components/ResourceListTable.vue +76 -101
  15. package/dist/spa/src/components/SkeleteLoader.vue +23 -0
  16. package/dist/spa/src/components/Toast.vue +18 -7
  17. package/dist/spa/src/components/ValueRenderer.vue +10 -4
  18. package/dist/spa/src/composables/useStores.ts +25 -11
  19. package/dist/spa/src/index.scss +2 -1
  20. package/dist/spa/src/main.ts +1 -1
  21. package/dist/spa/src/router/index.ts +0 -1
  22. package/dist/spa/src/stores/user.ts +21 -3
  23. package/dist/spa/src/types/AdminForthConfig.ts +1477 -0
  24. package/dist/spa/src/types/FrontendAPI.ts +121 -0
  25. package/dist/spa/src/utils.ts +6 -4
  26. package/dist/spa/src/views/CreateView.vue +2 -9
  27. package/dist/spa/src/views/EditView.vue +1 -9
  28. package/dist/spa/src/views/ListView.vue +17 -16
  29. package/dist/spa/src/views/LoginView.vue +57 -31
  30. package/dist/spa/src/views/ResourceParent.vue +1 -2
  31. package/dist/spa/src/views/ShowView.vue +17 -21
  32. package/dist/spa/tailwind.config.js +3 -1
  33. package/dist/spa/vite.config.ts +7 -0
  34. package/package.json +3 -4
@@ -0,0 +1,121 @@
1
+
2
+ export interface FrontendAPIInterface {
3
+
4
+ /**
5
+ * Show a confirmation dialog
6
+ *
7
+ * The dialog will be displayed to the user
8
+ *
9
+ * Example:
10
+ *
11
+ * ```ts
12
+ * const isConfirmed = await window.adminforth.confirm({message: 'Are you sure?', yes: 'Yes', no: 'No'})
13
+ * if (isConfirmed) {
14
+ * your code...
15
+ * }
16
+ * ```
17
+ *
18
+ * @param params - The parameters of the dialog
19
+ * @returns A promise that resolves when the user confirms the dialog
20
+ */
21
+ confirm(params:ConfirmParams ): Promise<void>;
22
+ /**
23
+ * Show an alert
24
+ *
25
+ * The alert will be displayed to the user
26
+ *
27
+ * Example:
28
+ *
29
+ * ```ts
30
+ * window.adminforth.alert({message: 'Hello', variant: 'success'})
31
+ * ```
32
+ *
33
+ * @param params - The parameters of the alert
34
+ */
35
+ alert(params:AlertParams): void;
36
+ /**
37
+ * Add a filter to the list of filters.
38
+ * Works only when user located on the list page.
39
+ * Can be used to set filter from charts or other components in pageInjections.
40
+ *
41
+ * Example:
42
+ *
43
+ * ```ts
44
+ * window.adminforth.updateListFilter({field: 'name', operator: 'ilike', value: 'john'})
45
+ * ```
46
+ *
47
+ * @param filter - The filter to add
48
+ */
49
+ setListFilter(filter: any): void;
50
+ /**
51
+ * Update a filter in the list of filters
52
+ *
53
+ * Example:
54
+ *
55
+ * ```ts
56
+ * window.adminforth.updateListFilter({field: 'name', operator: 'ilike', value: 'john'})
57
+ * ```
58
+ *
59
+ * @param filter - The filter to update
60
+ */
61
+ updateListFilter(filter: any): void;
62
+ /**
63
+ * Clear all filters from the list
64
+ */
65
+ clearListFilters(): void;
66
+
67
+ }
68
+
69
+ export type ConfirmParams = {
70
+ /**
71
+ * The message to display in the dialog
72
+ */
73
+ message?: string;
74
+ /**
75
+ * The text to display in the "accept" button
76
+ */
77
+ yes?: string;
78
+ /**
79
+ * The text to display in the "cancel" button
80
+ */
81
+ no?: string;
82
+
83
+ }
84
+
85
+ export type AlertParams = {
86
+ /**
87
+ * The message to display in the alert
88
+ */
89
+ message?: string;
90
+
91
+ /**
92
+ * The message to display in the alert as HTML (can be used instead of message)
93
+ */
94
+ messageHtml?: string;
95
+
96
+ /**
97
+ * The variant of the alert
98
+ */
99
+ variant?: AlertVariant | keyof typeof AlertVariant;
100
+
101
+ /**
102
+ * The timeout of the alert in seconds or 'unlimited' to keep the alert open until the user closes it.
103
+ * Default is 10 seconds;
104
+ */
105
+ timeout?: number | 'unlimited';
106
+
107
+ }
108
+
109
+
110
+
111
+ export enum AlertVariant {
112
+ danger = 'danger',
113
+ success = 'success',
114
+ warning = 'warning',
115
+ info = 'info'
116
+ }
117
+
118
+
119
+
120
+
121
+
@@ -6,7 +6,10 @@ import { useRouter } from 'vue-router';
6
6
  import { useCoreStore } from './stores/core';
7
7
  import { useUserStore } from './stores/user';
8
8
 
9
- export async function callApi({path, method, body=undefined} ) {
9
+ export async function callApi({path, method, body=undefined}: {
10
+ path: string, method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'
11
+ body?: any
12
+ }): Promise<any> {
10
13
  const options = {
11
14
  method,
12
15
  headers: {
@@ -16,7 +19,7 @@ export async function callApi({path, method, body=undefined} ) {
16
19
  };
17
20
  const fullPath = `${import.meta.env.VITE_ADMINFORTH_PUBLIC_PATH || ''}${path}`;
18
21
  const r = await fetch(fullPath, options);
19
- if (r.status == 401) {
22
+ if (r.status == 401 ) {
20
23
  useUserStore().unauthorize();
21
24
  router.push({ name: 'login' });
22
25
  return null;
@@ -39,7 +42,6 @@ export async function callAdminForthApi({ path, method, body=undefined }: {
39
42
 
40
43
  export function getCustomComponent({ file, meta }: { file: string, meta: any }) {
41
44
  const name = file.replace(/@/g, '').replace(/\./g, '').replace(/\//g, '');
42
- console.log('resolving name', name);
43
45
  return resolveComponent(name);
44
46
  }
45
47
 
@@ -71,7 +73,7 @@ export const loadFile = (file: string) => {
71
73
  return baseUrl;
72
74
  }
73
75
 
74
- export function checkEmptyValues(value: any, viewType:'show' | 'list' ) {
76
+ export function checkEmptyValues(value: any, viewType: 'show' | 'list' ) {
75
77
  const config: CoreConfig | {} = useCoreStore().config;
76
78
  let emptyFieldPlaceholder = '';
77
79
  if (config.emptyFieldPlaceholder) {
@@ -50,7 +50,7 @@
50
50
  @update:record="onUpdateRecord"
51
51
  @update:isValid="isValid = $event"
52
52
  :validating="validating"
53
- :customComponentsPerColumn="createComponentsPerColumn"
53
+ :source="'create'"
54
54
  >
55
55
  </ResourceForm>
56
56
 
@@ -91,7 +91,6 @@ const route = useRoute();
91
91
  const router = useRouter();
92
92
 
93
93
  const record = ref({});
94
- let createComponentsPerColumn = {};
95
94
 
96
95
  const coreStore = useCoreStore();
97
96
 
@@ -115,17 +114,12 @@ onMounted(async () => {
115
114
  await coreStore.fetchResourceFull({
116
115
  resourceId: route.params.resourceId
117
116
  });
118
- createComponentsPerColumn = coreStore.resource.columns.reduce((acc, column) => {
119
- if (column.components?.create) {
120
- acc[column.name] = getCustomComponent(column.components.create);
121
- }
122
- return acc;
123
- }, {});
124
117
  loading.value = false;
125
118
  checkAcessByAllowedActions(coreStore.resourceOptions.allowedActions,'create');
126
119
  });
127
120
 
128
121
  async function saveRecord() {
122
+ console.log('saveRecord isValid', isValid.value);
129
123
  if (!isValid.value) {
130
124
  validating.value = true;
131
125
  return;
@@ -143,7 +137,6 @@ async function saveRecord() {
143
137
  });
144
138
  if (response.error) {
145
139
  showErrorTost(response.error);
146
-
147
140
  }
148
141
  saving.value = false;
149
142
  if (route.query.returnTo) {
@@ -46,7 +46,7 @@
46
46
  @update:record="onUpdateRecord"
47
47
  @update:isValid="isValid = $event"
48
48
  :validating="validating"
49
- :customComponentsPerColumn="editComponentsPerColumn"
49
+ :source="'edit'"
50
50
  >
51
51
  </ResourceForm>
52
52
 
@@ -88,7 +88,6 @@ const loading = ref(false);
88
88
  const saving = ref(false);
89
89
 
90
90
  const record = ref({});
91
- let editComponentsPerColumn = {};
92
91
 
93
92
  async function onUpdateRecord(newRecord) {
94
93
  record.value = newRecord;
@@ -119,13 +118,6 @@ onMounted(async () => {
119
118
  primaryKey: route.params.primaryKey,
120
119
  });
121
120
  checkAcessByAllowedActions(coreStore.resourceOptions.allowedActions,'edit');
122
-
123
- editComponentsPerColumn = coreStore.resource.columns.reduce((acc, column) => {
124
- if (column.components?.edit) {
125
- acc[column.name] = getCustomComponent(column.components.edit);
126
- }
127
- return acc;
128
- }, {});
129
121
  loading.value = false;
130
122
  });
131
123
 
@@ -22,7 +22,7 @@
22
22
  v-if="checkboxes.length"
23
23
  data-tooltip-target="tooltip-remove-all"
24
24
  data-tooltip-placement="bottom"
25
- class="flex gap-1 items-center py-1 px-3 me-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 rounded-default"
25
+ class="flex gap-1 items-center py-1 px-3 me-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-darkListTable dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700 rounded-default"
26
26
  >
27
27
  <IconBanOutline class="w-5 h-5 "/>
28
28
 
@@ -70,7 +70,7 @@
70
70
  class="bg-red-100 text-red-800 text-xs font-medium me-2 px-2.5 py-0.5 rounded dark:bg-gray-700 dark:text-red-400 border border-red-400"
71
71
  v-if="filtersStore.filters.length">
72
72
  {{ filtersStore.filters.length }}
73
- </span>
73
+ </span>
74
74
  </button>
75
75
  </BreadcrumbsWithButtons>
76
76
 
@@ -89,6 +89,7 @@
89
89
  @update:sort="sort = $event"
90
90
  @update:checkboxes="checkboxes = $event"
91
91
  @update:records="getList"
92
+ :sort="sort"
92
93
  :pageSize="pageSize"
93
94
  :totalRows="totalRows"
94
95
  :checkboxes="checkboxes"
@@ -123,11 +124,7 @@ import { getCustomComponent } from '@/utils';
123
124
 
124
125
  import {
125
126
  IconBanOutline,
126
- IconEyeSolid,
127
127
  IconFilterOutline,
128
- IconPenSolid,
129
- IconTrashBinSolid,
130
- IconInboxOutline,
131
128
  IconPlusOutline
132
129
  } from '@iconify-prerendered/vue-flowbite';
133
130
 
@@ -136,13 +133,11 @@ import Filters from '@/components/Filters.vue';
136
133
  const filtersShow = ref(false);
137
134
 
138
135
  const coreStore = useCoreStore();
139
- const modalStore = useModalStore();
140
136
  const filtersStore = useFiltersStore();
141
137
 
142
138
  const route = useRoute();
143
139
 
144
140
  const page = ref(1);
145
- const filters = filtersStore.filters;
146
141
  const columnsMinMax = ref({});
147
142
  const sort = ref([]);
148
143
 
@@ -199,11 +194,6 @@ async function getList() {
199
194
  totalRows.value = data.total;
200
195
  }
201
196
 
202
-
203
-
204
-
205
-
206
-
207
197
  async function startBulkAction(actionId) {
208
198
  const data = await callAdminForthApi({
209
199
  path: '/start_bulk_action',
@@ -215,10 +205,13 @@ async function startBulkAction(actionId) {
215
205
 
216
206
  }
217
207
  });
218
- if (data?.status === 'success') {
208
+ if (data?.ok) {
219
209
  checkboxes.value = [];
210
+ await getList();
211
+ }
212
+ if (data?.error) {
213
+ showErrorTost(data.error);
220
214
  }
221
- await getList();
222
215
  }
223
216
 
224
217
 
@@ -229,6 +222,15 @@ async function init() {
229
222
  resourceId: route.params.resourceId
230
223
  });
231
224
 
225
+ if (coreStore.resource.options?.defaultSort) {
226
+ sort.value = [{
227
+ field: coreStore.resource.options.defaultSort.columnName,
228
+ direction: coreStore.resource.options.defaultSort.direction
229
+ }];
230
+ } else {
231
+ sort.value = [];
232
+ }
233
+
232
234
  await getList();
233
235
  columnsMinMax.value = await callAdminForthApi({
234
236
  path: '/get_min_max_for_columns',
@@ -250,7 +252,6 @@ onMounted(async () => {
250
252
  watch(() => route.params.resourceId, async () => {
251
253
  filtersStore.setFilters([]);
252
254
  checkboxes.value = [];
253
- sort.value = [];
254
255
  await init();
255
256
  });
256
257
 
@@ -22,17 +22,23 @@
22
22
  </div>
23
23
  <!-- Modal body -->
24
24
  <div class="p-4 md:p-5">
25
- <form class="space-y-4" @submit.prevent>
25
+ <form class="space-y-4" @submit.prevent>
26
26
  <div>
27
27
  <label for="username" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Your {{ coreStore.config?.usernameFieldName?.toLowerCase() }}</label>
28
- <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 />
29
32
  </div>
30
33
  <div class="relative">
31
34
  <label for="password" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Your password</label>
32
- <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 />
33
39
  <button type="button" @click="showPw = !showPw" class="absolute top-12 right-3 -translate-y-1/2 text-gray-400 dark:text-gray-300">
34
- <IconEyeSolid class="w-5 h-5" v-if="!showPw" />
35
- <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 />
36
42
  </button>
37
43
  </div>
38
44
  <!-- <div class="flex justify-between">
@@ -55,6 +61,17 @@
55
61
  </div>
56
62
  </div>
57
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
+
58
75
  <button
59
76
  @click="login"
60
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">
@@ -85,8 +102,9 @@ import { useUserStore } from '@/stores/user';
85
102
  import { IconEyeSolid, IconEyeSlashSolid } from '@iconify-prerendered/vue-flowbite';
86
103
  import { callAdminForthApi, loadFile } from '@/utils';
87
104
  import { useRouter } from 'vue-router';
88
- import { initFlowbite } from 'flowbite'
89
105
 
106
+ const passwordInput = ref(null);
107
+ const usernameInput = ref(null);
90
108
 
91
109
  const router = useRouter();
92
110
  const inProgress = ref(false);
@@ -99,35 +117,43 @@ const showPw = ref(false);
99
117
 
100
118
  const error = ref(null);
101
119
 
102
- onMounted(() => {
103
- 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();
104
129
  });
105
130
 
106
- async function login() {
107
- inProgress.value = true;
108
- const resp = await callAdminForthApi({
109
- path: '/login',
110
- method: 'POST',
111
- body: {
112
- username: document.getElementById('username').value,
113
- password: document.getElementById('password').value,
114
- }
115
- });
116
- inProgress.value = false;
117
- if (resp.error) {
118
- error.value = resp.error;
119
- } else {
120
- error.value = null;
121
- user.authorize()
122
- router.push('/');
123
- await router.isReady();
124
- await coreStore.fetchMenuAndResource();
125
- setTimeout(() => {
126
- initFlowbite();
127
- });
128
131
 
132
+ async function login() {
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,
129
146
  }
130
-
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
+ }
131
157
  }
132
158
 
133
159
 
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div :key="`${$route?.params.resourceId}---${$route?.params.primaryKey}`">
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
 
@@ -43,23 +43,23 @@
43
43
  <span class="sr-only">Loading...</span>
44
44
  </div>
45
45
  <div
46
- v-else
47
- class="relative overflow-x-auto shadow-resourse-form-shadow "
46
+ v-else-if="coreStore.record"
47
+ class="relative overflow-x-auto rounded-default shadow-resourseFormShadow "
48
48
  >
49
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-form-view-heading dark:bg-gray-700 dark:text-gray-400">
50
+ <thead class="text-xs text-gray-700 uppercase bg-lightFormHeading dark:bg-gray-700 dark:text-gray-400">
51
51
  <tr>
52
52
  <th scope="col" class="px-6 py-3">
53
53
  Field
54
54
  </th>
55
- <th scope="col" class="px-6 py-3 w-4/6">
55
+ <th scope="col" class="px-6 py-3 w-5/6">
56
56
  Value
57
57
  </th>
58
58
  </tr>
59
59
  </thead>
60
60
  <tbody>
61
61
  <tr v-for="column in coreStore.resource?.columns.filter(c => c.showIn.includes('show'))" :key="column.name"
62
- class="bg-form-view-bg odd:dark:bg-gray-900 even:dark:bg-gray-800 border-b dark:border-gray-700"
62
+ class="bg-lightForm bg-darkForm odd:dark:bg-gray-900 even:dark:bg-gray-800 border-b dark:border-gray-700"
63
63
  >
64
64
  <component
65
65
  v-if="column.components?.showRow"
@@ -75,8 +75,8 @@
75
75
  </td>
76
76
  <td class="px-6 py-4 whitespace-nowrap whitespace-pre-wrap">
77
77
  <component
78
- v-if="showComponentsPerColumn[column.name]"
79
- :is="showComponentsPerColumn[column.name]"
78
+ v-if="column?.components?.show"
79
+ :is="getCustomComponent(column?.components?.show)"
80
80
  :resource="coreStore.resource"
81
81
  :meta="column.components.show.meta"
82
82
  :column="column"
@@ -90,7 +90,12 @@
90
90
  </template>
91
91
  </tr>
92
92
  </tbody>
93
- </table>
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>
94
99
 
95
100
  <component
96
101
  v-for="c in coreStore?.resourceOptions?.pageInjections?.show?.bottom || []"
@@ -101,7 +106,6 @@
101
106
  :resource="coreStore.resource"
102
107
  :adminUser="coreStore.adminUser"
103
108
  />
104
- </div>
105
109
 
106
110
 
107
111
  </div>
@@ -120,14 +124,13 @@ import { IconPenSolid, IconTrashBinSolid } from '@iconify-prerendered/vue-flowbi
120
124
  import { onMounted, ref } from 'vue';
121
125
  import { useRoute,useRouter } from 'vue-router';
122
126
  import {callAdminForthApi} from '@/utils';
123
- import {showSuccesTost} from '@/composables/useFrontendApi';
127
+ import { showSuccesTost, showErrorTost } from '@/composables/useFrontendApi';
124
128
 
125
129
 
126
130
  const item = ref(null);
127
131
  const route = useRoute();
128
132
  const router = useRouter();
129
- const loading = ref(false);
130
- let showComponentsPerColumn = {};
133
+ const loading = ref(true);
131
134
 
132
135
  console.log(route.params,'showWiev');
133
136
 
@@ -145,13 +148,6 @@ onMounted(async () => {
145
148
  primaryKey: route.params.primaryKey,
146
149
  });
147
150
  checkAcessByAllowedActions(coreStore.resourceOptions.allowedActions,'show');
148
-
149
- showComponentsPerColumn = coreStore.resource.columns.reduce((acc, column) => {
150
- if (column.components?.show) {
151
- acc[column.name] = getCustomComponent(column.components.show);
152
- }
153
- return acc;
154
- }, {});
155
151
  loading.value = false;
156
152
  });
157
153
 
@@ -174,7 +170,7 @@ async function deleteRecord(row) {
174
170
  router.push({ name: 'resource-list', params: { resourceId: route.params.resourceId } });
175
171
  showSuccesTost('Record deleted successfully')
176
172
  } else {
177
- console.error(res.error)
173
+ showErrorTost(res.error)
178
174
  }
179
175
 
180
176
  } catch (e) {
@@ -185,4 +181,4 @@ async function deleteRecord(row) {
185
181
 
186
182
  }
187
183
 
188
- </script>
184
+ </script>
@@ -9,7 +9,9 @@ export default {
9
9
 
10
10
  darkMode: 'class',
11
11
  plugins: [
12
- require('flowbite/plugin'),
12
+ require('flowbite/plugin')({
13
+ charts: true,
14
+ }),
13
15
  ],
14
16
  }
15
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 = {
@@ -26,7 +27,13 @@ const customLogger = {
26
27
  clear() {
27
28
  console.clear();
28
29
  },
30
+ clearScreen() {
31
+ console.clear();
32
+ },
29
33
  hasWarned: false,
34
+ hasErrorLogged: (error: any): boolean => {
35
+ return false;
36
+ }
30
37
  };
31
38
 
32
39
  // https://vitejs.dev/config/
package/package.json CHANGED
@@ -1,17 +1,16 @@
1
1
  {
2
2
  "name": "adminforth",
3
- "version": "1.2.56",
3
+ "version": "1.2.58",
4
4
  "description": "OpenSource Vue3 powered forth-generation admin panel",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "scripts": {
8
8
  "test": "echo \"Error: no test specified\" && exit 1",
9
9
  "build": "tsc",
10
- "prepareDist": "cp -r spa dist/spa",
10
+ "prepareDist": "cp -rL spa dist/spa",
11
11
  "rollout": "tsc && npm run prepareDist && npm version patch && npm publish && npm run rollout-doc",
12
12
  "rollout-doc": "cd documentation && npm run build && npm run deploy",
13
- "docs": "typedoc",
14
- "postinstall": "ln -s types spa/src/types"
13
+ "docs": "typedoc"
15
14
  },
16
15
  "author": "devforth.io",
17
16
  "license": "ISC",