adminforth 3.6.4-next.1 → 3.6.4-next.10
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.
- package/dist/spa/src/afcl/Dropzone.vue +2 -2
- package/dist/spa/src/afcl/ProgressBar.vue +10 -8
- package/dist/spa/src/renderers/SensitiveBlurCell.vue +65 -15
- package/dist/spa/src/stores/toast.ts +3 -2
- package/dist/spa/src/utils/browserUuid.ts +42 -0
- package/dist/spa/src/utils/clientId.ts +3 -1
- package/dist/spa/src/views/LoginView.vue +1 -1
- package/package.json +1 -1
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
@dragleave.prevent="dragging = false"
|
|
6
6
|
@drop.prevent="dragging = false; doEmit(($event.dataTransfer as DataTransfer).files)"
|
|
7
7
|
>
|
|
8
|
-
<label :id="id" class="flex flex-col items-center justify-center w-full border-2 border-dashed rounded-
|
|
8
|
+
<label :id="id" class="flex flex-col items-center justify-center w-full border-2 border-dashed rounded-default cursor-pointer
|
|
9
9
|
hover:bg-lightDropzoneBackgroundHover hover:border-lightDropzoneBorderHover dark:hover:border-darkDropzoneBorderHover dark:hover:bg-darkDropzoneBackgroundHover"
|
|
10
10
|
:class="{
|
|
11
11
|
'border-lightDropzoneBorderDragging dark:border-darkDropzoneBorderDragging': dragging,
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
>
|
|
51
51
|
<template v-for="(file, index) in selectedFiles" :key="index">
|
|
52
52
|
<div
|
|
53
|
-
class="text-sm text-lightDropzoneIcon dark:text-darkDropzoneIcon bg-lightDropzoneBackgroundHover dark:bg-darkDropzoneBackgroundHover rounded-
|
|
53
|
+
class="text-sm text-lightDropzoneIcon dark:text-darkDropzoneIcon bg-lightDropzoneBackgroundHover dark:bg-darkDropzoneBackgroundHover rounded-default
|
|
54
54
|
flex items-center gap-1 px-2 py-1 group"
|
|
55
55
|
>
|
|
56
56
|
<IconFileSolid class="w-4 h-4 flex-shrink-0" />
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="relative w-full bg-lightProgressBarUnfilledColor rounded-full h-2.5 dark:bg-darkProgressBarUnfilledColor" :class="props.height ? `h-${props.height}` : ''">
|
|
3
|
-
<span v-if="leftLabel" class="absolute -top-6 left-0 text-sm
|
|
4
|
-
<span v-if="rightLabel" class="absolute -top-6 right-0 text-sm
|
|
5
|
-
<div
|
|
3
|
+
<span v-if="leftLabel" class="absolute -top-6 left-0 text-sm" :class="textClass">{{ leftLabel }}</span>
|
|
4
|
+
<span v-if="rightLabel" class="absolute -top-6 right-0 text-sm" :class="textClass">{{ rightLabel }}</span>
|
|
5
|
+
<div
|
|
6
6
|
class="bg-lightPrimary dark:bg-darkPrimary h-2.5 rounded-full transition-all duration-300 ease-in-out"
|
|
7
|
-
:class="{ 'progress-bar': showAnimation, [`h-${props.height}`]: props.height }"
|
|
7
|
+
:class="{ 'progress-bar': showAnimation, [`h-${props.height}`]: props.height }"
|
|
8
8
|
:style="{ width: `${percentage}%` }"
|
|
9
9
|
></div>
|
|
10
10
|
<div v-if="showValues || showProgress" class="flex justify-between mt-2">
|
|
11
|
-
<span v-if="showValues" class="text-sm
|
|
12
|
-
<span v-if="showProgress" class="text-sm
|
|
13
|
-
<span v-if="showValues" class="text-sm
|
|
11
|
+
<span v-if="showValues" class="text-sm" :class="textClass">{{ formatValue(minValue) }}</span>
|
|
12
|
+
<span v-if="showProgress" class="text-sm" :class="textClass">{{ progressText }}</span>
|
|
13
|
+
<span v-if="showValues" class="text-sm" :class="textClass">{{ formatValue(maxValue) }}</span>
|
|
14
14
|
</div>
|
|
15
15
|
</div>
|
|
16
16
|
</template>
|
|
@@ -31,6 +31,7 @@ interface Props {
|
|
|
31
31
|
showProgress?: boolean
|
|
32
32
|
showAnimation?: boolean
|
|
33
33
|
height?: number
|
|
34
|
+
textClass?: string
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
const props = withDefaults(defineProps<Props>(), {
|
|
@@ -39,7 +40,8 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
39
40
|
formatter: (value: number) => `${value}`,
|
|
40
41
|
progressFormatter: (value: number, percentage: number) => `${value}`,
|
|
41
42
|
showValues: true,
|
|
42
|
-
showProgress: true
|
|
43
|
+
showProgress: true,
|
|
44
|
+
textClass: 'text-lightProgressBarText dark:text-darkProgressBarText',
|
|
43
45
|
})
|
|
44
46
|
|
|
45
47
|
const percentage = computed((): number => {
|
|
@@ -1,39 +1,89 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="inline-flex items-center gap-1">
|
|
3
|
-
<
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
<span
|
|
9
|
-
class="cursor-pointer select-none transition-all duration-200 text-lightListTableText dark:text-darkListTableText"
|
|
10
|
-
:class="{
|
|
11
|
-
'blur-[7px] hover:blur-[5px] brightness-50 dark:brightness-150': !show,
|
|
12
|
-
}"
|
|
3
|
+
<Tooltip>
|
|
4
|
+
<div
|
|
5
|
+
class="overflow-hidden max-h-[20px] rounded-default"
|
|
6
|
+
:class="{ 'shrink-0 w-[90px]': compact }"
|
|
7
|
+
@click="toggle"
|
|
13
8
|
>
|
|
14
|
-
<
|
|
15
|
-
|
|
16
|
-
|
|
9
|
+
<span
|
|
10
|
+
class="cursor-pointer select-none transition-all duration-200 text-lightListTableText dark:text-darkListTableText"
|
|
11
|
+
:class="{
|
|
12
|
+
'block truncate': compact,
|
|
13
|
+
'blur-[7px] hover:blur-[5px] brightness-50 dark:brightness-150': !show,
|
|
14
|
+
}"
|
|
15
|
+
>
|
|
16
|
+
<template v-if="compact">{{ visualValue }}</template>
|
|
17
|
+
<ValueRenderer v-else :column="column" :record="record" />
|
|
18
|
+
</span>
|
|
19
|
+
</div>
|
|
20
|
+
<template #tooltip>
|
|
21
|
+
<span class="whitespace-nowrap">{{ tooltipText }}</span>
|
|
22
|
+
</template>
|
|
23
|
+
</Tooltip>
|
|
24
|
+
|
|
25
|
+
<span v-if="showCopy && rawValue" class="shrink-0 w-5 h-5">
|
|
26
|
+
<IconFileCopyAltSolid
|
|
27
|
+
v-if="show"
|
|
28
|
+
@click.stop="copyToCB"
|
|
29
|
+
class="w-5 h-5 cursor-pointer text-lightPrimary dark:text-darkPrimary"
|
|
30
|
+
/>
|
|
31
|
+
</span>
|
|
17
32
|
</div>
|
|
18
33
|
</template>
|
|
19
34
|
|
|
20
35
|
<script setup lang="ts">
|
|
21
|
-
import { ref } from 'vue';
|
|
36
|
+
import { ref, computed } from 'vue';
|
|
37
|
+
import { useI18n } from 'vue-i18n';
|
|
38
|
+
import { IconFileCopyAltSolid } from '@iconify-prerendered/vue-flowbite';
|
|
22
39
|
import ValueRenderer from '@/components/ValueRenderer.vue';
|
|
40
|
+
import Tooltip from '@/afcl/Tooltip.vue';
|
|
41
|
+
import { useAdminforth } from '@/adminforth';
|
|
23
42
|
import type { AdminForthResourceColumnCommon, AdminForthResourceCommon, AdminUser } from '@/types/Common';
|
|
24
43
|
|
|
25
44
|
const props = defineProps<{
|
|
26
45
|
column: AdminForthResourceColumnCommon;
|
|
27
46
|
record: any;
|
|
28
|
-
meta: any;
|
|
47
|
+
meta: { compact?: boolean; copy?: boolean } | any;
|
|
29
48
|
resource: AdminForthResourceCommon;
|
|
30
49
|
adminUser: AdminUser;
|
|
31
50
|
}>();
|
|
32
51
|
|
|
52
|
+
const { t } = useI18n();
|
|
53
|
+
const { alert } = useAdminforth();
|
|
54
|
+
|
|
33
55
|
const show = ref(false);
|
|
34
56
|
|
|
57
|
+
const rawValue = computed(() => props.record[props.column.name]);
|
|
58
|
+
|
|
59
|
+
const compact = computed(() => props.meta?.compact);
|
|
60
|
+
const showCopy = computed(() => compact.value && props.meta?.copy);
|
|
61
|
+
const visualValue = computed(() => {
|
|
62
|
+
const val = rawValue.value;
|
|
63
|
+
if (val && String(val).length > 8) {
|
|
64
|
+
const s = String(val);
|
|
65
|
+
return `${s.slice(0, 4)}...${s.slice(-4)}`;
|
|
66
|
+
}
|
|
67
|
+
return val;
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const tooltipText = computed(() => {
|
|
71
|
+
if (compact.value && show.value) {
|
|
72
|
+
return rawValue.value;
|
|
73
|
+
}
|
|
74
|
+
return show.value ? t('Click to hide') : t('Click to show');
|
|
75
|
+
});
|
|
76
|
+
|
|
35
77
|
function toggle(event: MouseEvent) {
|
|
36
78
|
show.value = !show.value;
|
|
37
79
|
event.stopPropagation();
|
|
38
80
|
}
|
|
81
|
+
|
|
82
|
+
function copyToCB() {
|
|
83
|
+
navigator.clipboard.writeText(rawValue.value);
|
|
84
|
+
alert({
|
|
85
|
+
message: t('Copied to clipboard'),
|
|
86
|
+
variant: 'success',
|
|
87
|
+
});
|
|
88
|
+
}
|
|
39
89
|
</script>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ref, watch, type Ref } from 'vue'
|
|
2
2
|
import { defineStore } from 'pinia'
|
|
3
3
|
import { useRoute } from 'vue-router';
|
|
4
|
+
import { randomBrowserUUID } from '@/utils/browserUuid';
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
|
|
@@ -22,7 +23,7 @@ export const useToastStore = defineStore('toast', () => {
|
|
|
22
23
|
buttons?: { value: any; label: string }[];
|
|
23
24
|
onResolve?: (value?: any) => void;
|
|
24
25
|
}): string => {
|
|
25
|
-
const toastId =
|
|
26
|
+
const toastId = randomBrowserUUID();
|
|
26
27
|
toasts.value.push({
|
|
27
28
|
...toast,
|
|
28
29
|
id: toastId,
|
|
@@ -45,4 +46,4 @@ export const useToastStore = defineStore('toast', () => {
|
|
|
45
46
|
};
|
|
46
47
|
|
|
47
48
|
return { toasts, addToast, removeToast, resolveToast };
|
|
48
|
-
});
|
|
49
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const UUID_BYTE_LENGTH = 16;
|
|
2
|
+
|
|
3
|
+
const UUID_HEX = Array.from(
|
|
4
|
+
{ length: 256 },
|
|
5
|
+
(_, index) => index.toString(16).padStart(2, '0'),
|
|
6
|
+
);
|
|
7
|
+
|
|
8
|
+
export function randomBrowserUUID(): string {
|
|
9
|
+
if (typeof crypto.randomUUID === 'function') {
|
|
10
|
+
return crypto.randomUUID();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const bytes = crypto.getRandomValues(
|
|
14
|
+
new Uint8Array(UUID_BYTE_LENGTH),
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
bytes[6] = (bytes[6] & 0x0f) | 0x40;
|
|
18
|
+
bytes[8] = (bytes[8] & 0x3f) | 0x80;
|
|
19
|
+
|
|
20
|
+
return [
|
|
21
|
+
UUID_HEX[bytes[0]],
|
|
22
|
+
UUID_HEX[bytes[1]],
|
|
23
|
+
UUID_HEX[bytes[2]],
|
|
24
|
+
UUID_HEX[bytes[3]],
|
|
25
|
+
'-',
|
|
26
|
+
UUID_HEX[bytes[4]],
|
|
27
|
+
UUID_HEX[bytes[5]],
|
|
28
|
+
'-',
|
|
29
|
+
UUID_HEX[bytes[6]],
|
|
30
|
+
UUID_HEX[bytes[7]],
|
|
31
|
+
'-',
|
|
32
|
+
UUID_HEX[bytes[8]],
|
|
33
|
+
UUID_HEX[bytes[9]],
|
|
34
|
+
'-',
|
|
35
|
+
UUID_HEX[bytes[10]],
|
|
36
|
+
UUID_HEX[bytes[11]],
|
|
37
|
+
UUID_HEX[bytes[12]],
|
|
38
|
+
UUID_HEX[bytes[13]],
|
|
39
|
+
UUID_HEX[bytes[14]],
|
|
40
|
+
UUID_HEX[bytes[15]],
|
|
41
|
+
].join('');
|
|
42
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { randomBrowserUUID } from './browserUuid';
|
|
2
|
+
|
|
1
3
|
const ADMINFORTH_CLIENT_ID_STORAGE_KEY = 'adminforthClientId';
|
|
2
4
|
|
|
3
5
|
export const ADMINFORTH_CLIENT_ID_HEADER = 'x-adminforth-client-id';
|
|
@@ -8,7 +10,7 @@ export function getAdminForthClientId(): string {
|
|
|
8
10
|
return existingClientId;
|
|
9
11
|
}
|
|
10
12
|
|
|
11
|
-
const clientId =
|
|
13
|
+
const clientId = randomBrowserUUID();
|
|
12
14
|
sessionStorage.setItem(ADMINFORTH_CLIENT_ID_STORAGE_KEY, clientId);
|
|
13
15
|
return clientId;
|
|
14
16
|
}
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
overflow-x-hidden z-50 min-w-[350px] justify-center items-center md:inset-0 h-[calc(100%-1rem)] max-h-full">
|
|
33
33
|
<div class="relative p-4 w-full max-h-full max-w-[400px]">
|
|
34
34
|
<!-- Modal content -->
|
|
35
|
-
<div class="af-login-popup af-login-modal-content relative bg-lightLoginViewBackground rounded-
|
|
35
|
+
<div class="af-login-popup af-login-modal-content relative bg-lightLoginViewBackground rounded-default shadow dark:bg-darkLoginViewBackground dark:shadow-black" :class=" { 'rounded-b-none overflow-hidden': error } ">
|
|
36
36
|
<!-- Modal header -->
|
|
37
37
|
<div class="af-login-modal-header flex items-center justify-between flex-col p-4 md:p-5 border-b rounded-t dark:border-gray-600">
|
|
38
38
|
|