Add more permissions

This commit is contained in:
Simon Pocrnjič
2025-10-31 10:16:38 +01:00
parent 7d4d18143d
commit ed4f67effb
18 changed files with 404 additions and 193 deletions
+45 -28
View File
@@ -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>
+28 -40
View File
@@ -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>
+21 -11
View File
@@ -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);