Add more permissions
This commit is contained in:
@@ -1,22 +1,22 @@
|
||||
<script setup>
|
||||
import { watch, onMounted } from 'vue'
|
||||
import { useCurrencyInput } from 'vue-currency-input'
|
||||
import { watch, onMounted } from "vue";
|
||||
import { useCurrencyInput } from "vue-currency-input";
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: { type: [Number, String, null], default: null },
|
||||
id: String,
|
||||
name: String,
|
||||
placeholder: { type: String, default: '0,00' },
|
||||
placeholder: { type: String, default: "0,00" },
|
||||
disabled: Boolean,
|
||||
required: Boolean,
|
||||
currency: { type: String, default: 'EUR' },
|
||||
locale: { type: String, default: 'sl-SI' },
|
||||
currency: { type: String, default: "EUR" },
|
||||
locale: { type: String, default: "sl-SI" },
|
||||
precision: { type: [Number, Object], default: 2 },
|
||||
allowNegative: { type: Boolean, default: false },
|
||||
useGrouping: { type: Boolean, default: true },
|
||||
})
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
const emit = defineEmits(["update:modelValue", "change"]);
|
||||
|
||||
const { inputRef, numberValue, setValue, setOptions } = useCurrencyInput({
|
||||
currency: props.currency,
|
||||
@@ -24,35 +24,51 @@ const { inputRef, numberValue, setValue, setOptions } = useCurrencyInput({
|
||||
precision: props.precision,
|
||||
useGrouping: props.useGrouping,
|
||||
valueRange: props.allowNegative ? {} : { min: 0 },
|
||||
})
|
||||
});
|
||||
|
||||
watch(() => props.modelValue, (val) => {
|
||||
const numeric = typeof val === 'string' ? parseFloat(val) : val
|
||||
if (numeric !== numberValue.value) {
|
||||
setValue(isNaN(numeric) ? null : numeric)
|
||||
}
|
||||
}, { immediate: true })
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(val) => {
|
||||
const numeric = typeof val === "string" ? parseFloat(val) : val;
|
||||
if (numeric !== numberValue.value) {
|
||||
setValue(isNaN(numeric) ? null : numeric);
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
watch(numberValue, (val) => {
|
||||
emit('update:modelValue', val)
|
||||
})
|
||||
emit("update:modelValue", val);
|
||||
});
|
||||
|
||||
watch(() => [props.currency, props.locale, props.precision, props.useGrouping, props.allowNegative], () => {
|
||||
setOptions({
|
||||
currency: props.currency,
|
||||
locale: props.locale,
|
||||
precision: props.precision,
|
||||
useGrouping: props.useGrouping,
|
||||
valueRange: props.allowNegative ? {} : { min: 0 },
|
||||
})
|
||||
})
|
||||
watch(
|
||||
() => [
|
||||
props.currency,
|
||||
props.locale,
|
||||
props.precision,
|
||||
props.useGrouping,
|
||||
props.allowNegative,
|
||||
],
|
||||
() => {
|
||||
setOptions({
|
||||
currency: props.currency,
|
||||
locale: props.locale,
|
||||
precision: props.precision,
|
||||
useGrouping: props.useGrouping,
|
||||
valueRange: props.allowNegative ? {} : { min: 0 },
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
if (props.modelValue != null) {
|
||||
const numeric = typeof props.modelValue === 'string' ? parseFloat(props.modelValue) : props.modelValue
|
||||
setValue(isNaN(numeric) ? null : numeric)
|
||||
const numeric =
|
||||
typeof props.modelValue === "string"
|
||||
? parseFloat(props.modelValue)
|
||||
: props.modelValue;
|
||||
setValue(isNaN(numeric) ? null : numeric);
|
||||
}
|
||||
})
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -67,5 +83,6 @@ onMounted(() => {
|
||||
:required="required"
|
||||
class="mt-1 block w-full rounded-md border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 dark:bg-gray-800 dark:border-gray-600"
|
||||
autocomplete="off"
|
||||
@change="$emit('change', numberValue)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -2,23 +2,24 @@
|
||||
import InputLabel from "./InputLabel.vue";
|
||||
import InputError from "./InputError.vue";
|
||||
import { computed } from "vue";
|
||||
import VueDatePicker from "@vuepic/vue-datepicker";
|
||||
import "@vuepic/vue-datepicker/dist/main.css";
|
||||
|
||||
/*
|
||||
DatePickerField (v-calendar)
|
||||
- A thin wrapper around <VDatePicker> with a label and error support.
|
||||
- Uses v-calendar which handles popovers/teleport well inside modals.
|
||||
API: kept compatible with previous usage where possible.
|
||||
DatePickerField (vue-datepicker wrapper)
|
||||
- Replaces previous v-calendar usage to avoid range/dayIndex runtime errors.
|
||||
- Keeps API compatible with existing callers.
|
||||
Props:
|
||||
- modelValue: Date | string | number | null
|
||||
- id: string
|
||||
- label: string
|
||||
- format: string (default 'dd.MM.yyyy')
|
||||
- enableTimePicker: boolean (default false)
|
||||
- inline: boolean (default false) // When true, keeps the popover visible
|
||||
- inline: boolean (default false)
|
||||
- placeholder: string
|
||||
- error: string | string[]
|
||||
Note: Props like teleportTarget/autoPosition/menuClassName/fixed/closeOn... were for the old picker
|
||||
and are accepted for compatibility but are not used by v-calendar.
|
||||
- legacy props (teleportTarget, autoPosition, menuClassName, fixed, closeOnAutoApply, closeOnScroll)
|
||||
are accepted for compatibility but only mapped where applicable.
|
||||
*/
|
||||
|
||||
const props = defineProps({
|
||||
@@ -28,7 +29,7 @@ const props = defineProps({
|
||||
format: { type: String, default: "dd.MM.yyyy" },
|
||||
enableTimePicker: { type: Boolean, default: false },
|
||||
inline: { type: Boolean, default: false },
|
||||
// legacy/unused in v-calendar (kept to prevent breaking callers)
|
||||
// legacy props maintained for compatibility
|
||||
autoApply: { type: Boolean, default: false },
|
||||
teleportTarget: { type: [Boolean, String], default: "body" },
|
||||
autoPosition: { type: Boolean, default: true },
|
||||
@@ -50,44 +51,31 @@ const valueProxy = computed({
|
||||
},
|
||||
});
|
||||
|
||||
// Convert common date mask from lowercase tokens to v-calendar tokens
|
||||
const inputMask = computed(() => {
|
||||
let m = props.format || "dd.MM.yyyy";
|
||||
return (
|
||||
m.replace(/yyyy/g, "YYYY").replace(/dd/g, "DD").replace(/MM/g, "MM") +
|
||||
(props.enableTimePicker ? " HH:mm" : "")
|
||||
);
|
||||
});
|
||||
|
||||
const popoverCfg = computed(() => ({
|
||||
visibility: props.inline ? "visible" : "click",
|
||||
placement: "bottom-start",
|
||||
}));
|
||||
// vue-datepicker accepts format like 'dd.MM.yyyy' and controls 24h time via is-24 prop
|
||||
const inputClasses =
|
||||
"mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="col-span-6 sm:col-span-4">
|
||||
<InputLabel v-if="label" :for="id" :value="label" />
|
||||
|
||||
<!-- VCalendar DatePicker with custom input to keep Tailwind styling -->
|
||||
<VDatePicker
|
||||
<VueDatePicker
|
||||
v-model="valueProxy"
|
||||
:mode="enableTimePicker ? 'dateTime' : 'date'"
|
||||
:masks="{ input: inputMask }"
|
||||
:popover="popoverCfg"
|
||||
:is24hr="true"
|
||||
>
|
||||
<template #default="{ inputValue, inputEvents }">
|
||||
<input
|
||||
:id="id"
|
||||
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500"
|
||||
:placeholder="placeholder"
|
||||
:value="inputValue"
|
||||
autocomplete="off"
|
||||
v-on="inputEvents"
|
||||
/>
|
||||
</template>
|
||||
</VDatePicker>
|
||||
:format="format"
|
||||
:enable-time-picker="enableTimePicker"
|
||||
:is-24="true"
|
||||
:inline="inline"
|
||||
:auto-apply="autoApply"
|
||||
:teleport="false"
|
||||
:close-on-auto-apply="closeOnAutoApply"
|
||||
:close-on-scroll="closeOnScroll"
|
||||
:input-class-name="inputClasses"
|
||||
:menu-class-name="'z-[1000]'"
|
||||
:locale="'sl'"
|
||||
:id="id"
|
||||
:placeholder="placeholder"
|
||||
/>
|
||||
|
||||
<template v-if="error">
|
||||
<InputError
|
||||
@@ -102,5 +90,5 @@ const popoverCfg = computed(() => ({
|
||||
</template>
|
||||
|
||||
<style>
|
||||
/* Ensure the date picker menu overlays modals/dialogs */
|
||||
/* vue-datepicker provides its own menu layering; base CSS imported above */
|
||||
</style>
|
||||
|
||||
@@ -19,9 +19,13 @@ import { router, usePage } from "@inertiajs/vue3";
|
||||
|
||||
const props = defineProps({
|
||||
person: Object,
|
||||
personEdit: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
edit: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
default: true,
|
||||
},
|
||||
tabColor: {
|
||||
type: String,
|
||||
@@ -584,7 +588,7 @@ const submitSms = () => {
|
||||
<CusTab name="person" title="Oseba">
|
||||
<div class="flex justify-end mb-2">
|
||||
<span class="border-b-2 border-gray-500 hover:border-gray-800">
|
||||
<button @click="openDrawerUpdateClient">
|
||||
<button @click="openDrawerUpdateClient" v-if="edit && personEdit">
|
||||
<UserEditIcon size="lg" css="text-gray-500 hover:text-gray-800" />
|
||||
</button>
|
||||
</span>
|
||||
@@ -636,7 +640,7 @@ const submitSms = () => {
|
||||
</CusTab>
|
||||
<CusTab name="addresses" title="Naslovi">
|
||||
<div class="flex justify-end mb-2">
|
||||
<span class="border-b-2 border-gray-500 hover:border-gray-800">
|
||||
<span class="border-b-2 border-gray-500 hover:border-gray-800" v-if="edit">
|
||||
<button>
|
||||
<PlusIcon
|
||||
@click="openDrawerAddAddress(false, 0)"
|
||||
@@ -653,7 +657,7 @@ const submitSms = () => {
|
||||
<FwbBadge type="yellow">{{ address.country }}</FwbBadge>
|
||||
<FwbBadge>{{ address.type.name }}</FwbBadge>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="flex items-center gap-2" v-if="edit">
|
||||
<button>
|
||||
<EditIcon
|
||||
@click="openDrawerAddAddress(true, address.id)"
|
||||
@@ -678,7 +682,7 @@ const submitSms = () => {
|
||||
</CusTab>
|
||||
<CusTab name="phones" title="Telefonske">
|
||||
<div class="flex justify-end mb-2">
|
||||
<span class="border-b-2 border-gray-500 hover:border-gray-800">
|
||||
<span class="border-b-2 border-gray-500 hover:border-gray-800" v-if="edit">
|
||||
<button>
|
||||
<PlusIcon
|
||||
@click="operDrawerAddPhone(false, 0)"
|
||||
@@ -707,14 +711,14 @@ const submitSms = () => {
|
||||
>
|
||||
SMS
|
||||
</button>
|
||||
<button>
|
||||
<button v-if="edit">
|
||||
<EditIcon
|
||||
@click="operDrawerAddPhone(true, phone.id)"
|
||||
size="md"
|
||||
css="text-gray-500 hover:text-gray-800"
|
||||
/>
|
||||
</button>
|
||||
<button @click="openConfirm('phone', phone.id, phone.nu)">
|
||||
<button @click="openConfirm('phone', phone.id, phone.nu)" v-if="edit">
|
||||
<TrashBinIcon size="md" css="text-red-600 hover:text-red-700" />
|
||||
</button>
|
||||
</div>
|
||||
@@ -725,7 +729,7 @@ const submitSms = () => {
|
||||
</CusTab>
|
||||
<CusTab name="emails" title="Email">
|
||||
<div class="flex justify-end mb-2">
|
||||
<span class="border-b-2 border-gray-500 hover:border-gray-800">
|
||||
<span class="border-b-2 border-gray-500 hover:border-gray-800" v-if="edit">
|
||||
<button>
|
||||
<PlusIcon
|
||||
@click="openDrawerAddEmail(false, 0)"
|
||||
@@ -742,7 +746,10 @@ const submitSms = () => {
|
||||
v-for="(email, idx) in getEmails(person)"
|
||||
:key="idx"
|
||||
>
|
||||
<div class="text-sm leading-5 md:text-sm text-gray-500 flex justify-between">
|
||||
<div
|
||||
class="text-sm leading-5 md:text-sm text-gray-500 flex justify-between"
|
||||
v-if="edit"
|
||||
>
|
||||
<div class="flex gap-2">
|
||||
<FwbBadge v-if="email?.label">{{ email.label }}</FwbBadge>
|
||||
<FwbBadge v-else type="indigo">Email</FwbBadge>
|
||||
@@ -777,7 +784,7 @@ const submitSms = () => {
|
||||
</CusTab>
|
||||
<CusTab name="trr" title="TRR">
|
||||
<div class="flex justify-end mb-2">
|
||||
<span class="border-b-2 border-gray-500 hover:border-gray-800">
|
||||
<span class="border-b-2 border-gray-500 hover:border-gray-800" v-if="edit">
|
||||
<button>
|
||||
<PlusIcon
|
||||
@click="openDrawerAddTrr(false, 0)"
|
||||
@@ -794,7 +801,10 @@ const submitSms = () => {
|
||||
v-for="(acc, idx) in getTRRs(person)"
|
||||
:key="idx"
|
||||
>
|
||||
<div class="text-sm leading-5 md:text-sm text-gray-500 flex justify-between">
|
||||
<div
|
||||
class="text-sm leading-5 md:text-sm text-gray-500 flex justify-between"
|
||||
v-if="edit"
|
||||
>
|
||||
<div class="flex gap-2">
|
||||
<FwbBadge v-if="acc?.bank_name">{{ acc.bank_name }}</FwbBadge>
|
||||
<FwbBadge v-if="acc?.holder_name" type="indigo">{{
|
||||
|
||||
@@ -10,6 +10,12 @@ defineProps({
|
||||
type: String,
|
||||
default: 'bg-blue-500'
|
||||
},
|
||||
// Optional text color utility class (e.g., 'text-white', 'text-gray-800').
|
||||
// Left empty by default because the base class already includes 'text-white'.
|
||||
color: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
});
|
||||
|
||||
const isHover = ref(false);
|
||||
|
||||
Reference in New Issue
Block a user