Update various configuration files, components, and assets; enhance notification system and API endpoints; improve documentation and styles across the application.

This commit is contained in:
Haqeem Solehan
2025-10-16 16:05:39 +08:00
commit b124ff8092
336 changed files with 94392 additions and 0 deletions

View File

@@ -0,0 +1,13 @@
<script setup>
const props = defineProps({
context: Object,
});
function handleInput(e) {
props.context.node.input(e.target.value);
}
</script>
<template>
<input @input="handleInput" :value="props.context._value" />
</template>

View File

@@ -0,0 +1,139 @@
<script setup>
/* eslint-disable */
import { useDropzone } from "vue3-dropzone";
const props = defineProps({
context: Object,
});
const fileBase64 = ref([]);
const files = ref([]);
let err = ref(false);
let errmsg = ref("");
const accept = props.context.accept;
const multiple = props.context.multiple;
const maxSize = props.context.maxSize;
const minSize = props.context.minSize;
const maxFiles = props.context.maxFiles;
const disabled = props.context.disabled;
const toBase64 = (file) =>
new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = (error) => reject(error);
});
async function onDrop(fileList, fileError, event) {
if (fileError.length == 0) {
err.value = false;
errmsg.value = "";
for (let i = 0; i < fileList.length; i++) {
const base64 = await toBase64(fileList[i]);
fileBase64.value.push({ data: fileList[i], base64 });
files.value.push([fileList[i]]);
}
} else {
err.value = true;
errmsg.value = fileError[0].errors[0].message;
}
updateNodeValue();
}
async function removeFiles(index) {
fileBase64.value.splice(index, 1);
files.value.splice(index, 1);
updateNodeValue();
}
function updateNodeValue() {
props.context.node.input(files.value);
}
const { getRootProps, getInputProps, isDragActive } = useDropzone({
onDrop,
accept,
multiple: multiple === "true" ? true : false,
maxSize: maxSize ? parseInt(maxSize) : Infinity,
minSize: minSize ? parseInt(minSize) : 0,
maxFiles: maxFiles ? parseInt(maxFiles) : 0,
disabled: disabled === "true" ? true : false,
});
</script>
<template>
<!-- eslint-disable -->
<div :class="context.classes.dropzone">
<div v-bind="getRootProps()" class="cursor-pointer">
<input v-bind="getInputProps()" />
<div class="flex items-center justify-center h-36">
<div>
<Icon
class="!block m-auto mb-3"
size="30px"
name="ic:outline-upload-file"
/>
<p class="text-center" v-if="isDragActive">Drop the files here ...</p>
<p v-else>Drop files or click here to upload files</p>
</div>
</div>
</div>
<div
id="fileList"
class="grid sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4 gap-4"
v-auto-animate
>
<div
v-for="(file, index) in fileBase64"
class="relative overflow-hidden w-full h-20 md:h-36 rounded-lg border-2 border-[rgb(var(--border-color))]"
v-auto-animate
>
<img
v-if="file.data.type.includes('image')"
:src="file.base64"
class="w-full h-20 md:h-36 object-cover object-center rounded-lg"
/>
<div
v-else
class="h-full flex items-center justify-center opacity-50 text-primary font-semibold uppercase text-xl whitespace-nowrap"
>
{{
file.data.name.slice(
((file.data.name.lastIndexOf(".") - 1) >>> 0) + 2
)
}}
</div>
<Icon
name="ic:round-close"
@click="removeFiles(index)"
class="cursor-pointer absolute top-1 right-1 text-[rgb(var(--text-color))] bg-[rgb(var(--bg-2))] p-1 rounded-full"
size="18"
/>
<div
class="absolute bottom-1 right-1 bg-[rgb(var(--bg-2))] px-2 rounded-lg"
>
<span class="font-semibold text-xs text-[rgb(var(--text-color))]">
{{ file.data.path }}
</span>
</div>
</div>
</div>
<ul
v-if="err"
class="formkit-messages list-none p-0 mt-1 mb-0 relative -bottom-5 -left-2"
aria-live="polite"
>
<li
class="formkit-message text-red-500 mb-1 text-xs formkit-invalid:text-red-500 dark:formkit-invalid:text-danger"
id="input_9-rule_required"
data-message-type="validation"
>
{{ errmsg }}
</li>
</ul>
</div>
</template>

View File

@@ -0,0 +1,83 @@
<script setup>
/* eslint-disable */
const props = defineProps({
context: Object,
});
const digits = Number(props.context.digits);
const tmp = ref(props.context.value || "");
/**
* Handle input, advancing or retreating focus.
*/
function handleInput(index, e) {
const prev = tmp.value;
if (tmp.value.length <= index) {
// If this is a new digit
tmp.value = "" + tmp.value + e.target.value;
} else {
// If this digit is in the middle somewhere, cut the string into two
// pieces at the index, and insert our new digit in.
tmp.value =
"" +
tmp.value.substr(0, index) +
e.target.value +
tmp.value.substr(index + 1);
}
// Get all the digit inputs
const inputs = e.target.parentElement.querySelectorAll("input");
if (index < digits - 1 && tmp.value.length >= prev.length) {
// If this is a new input and not at the end, focus the next input
inputs.item(index + 1).focus();
} else if (index > 0 && tmp.value.length < prev.length) {
// in this case we deleted a value, focus backwards
inputs.item(index - 1).focus();
}
if (tmp.value.length === digits) {
// If our input is complete, commit the value.
props.context.node.input(tmp.value);
} else if (tmp.value.length < digits && props.context.value !== "") {
// If our input is incomplete, it should have no value.
props.context.node.input("");
}
}
/**
* On focus, select the text in our input.
*/
function handleFocus(e) {
e.target.select();
}
/**
* Handle the paste event.
*/
function handlePaste(e) {
const paste = e.clipboardData.getData("text");
if (typeof paste === "string") {
// If it is the right length, paste it.
tmp.value = paste.substr(0, digits);
const inputs = e.target.parentElement.querySelectorAll("input");
// Focus on the last character
inputs.item(tmp.value.length - 1).focus();
}
}
</script>
<template>
<!-- eslint-disable -->
<input
v-for="index in digits"
maxlength="1"
:class="context.classes.digit"
:value="tmp[index - 1] || ''"
@input="handleInput(index - 1, $event)"
@focus="handleFocus"
@paste="handlePaste"
/>
</template>

View File

@@ -0,0 +1,23 @@
<script setup>
/* eslint-disable */
const props = defineProps({
context: Object,
});
const mask = String(props.context.mask);
// console.log(props.context);
function handleInput(e) {
props.context.node.input(e.target.value);
}
</script>
<template>
<input
@input="handleInput"
:class="context.classes.input"
:value="props.context._value"
:placeholder="props.context.attrs.placeholder"
v-maska="mask"
/>
</template>

View File

@@ -0,0 +1,36 @@
<script setup>
const props = defineProps({
context: Object,
});
function handleChange(event) {
props.context.node.input(event.target.checked);
}
</script>
<template>
<label
:class="context.classes.toggle"
class="inline-flex items-center mb-5 cursor-pointer mt-1"
>
<input
type="checkbox"
:checked="context.value"
:disabled="context.disabled"
class="sr-only peer"
@change="handleChange"
/>
<div
class="relative w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-2 peer-focus:ring-blue-300 rounded-full peer peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:w-5 after:h-5 after:transition-all peer-checked:bg-blue-600"
></div>
<span class="ms-3 text-sm font-medium text-gray-900">
{{
context.onLabel || context.offLabel
? context.value
? context.onLabel
: context.offLabel
: context.label
}}
</span>
</label>
</template>