add billable rate updates for time entries in the past to projects and project members, fixes ST-304

This commit is contained in:
Gregor Vostrak
2024-07-04 18:30:47 +02:00
parent 7fd5d25781
commit e3b4cfd881
13 changed files with 480 additions and 32 deletions

View File

@@ -0,0 +1,80 @@
<script setup lang="ts">
import SecondaryButton from '@/Components/SecondaryButton.vue';
import DialogModal from '@/Components/DialogModal.vue';
import PrimaryButton from '@/Components/PrimaryButton.vue';
import { formatCents } from '../../../utils/money';
import { ArrowTopRightOnSquareIcon } from '@heroicons/vue/24/solid';
const show = defineModel('show', { default: false });
const saving = defineModel('saving', { default: false });
defineProps<{
newBillableRate?: number | null;
projectName: string;
}>();
const emit = defineEmits<{
submit: [billable_rate_update_time_entries: boolean];
}>();
</script>
<template>
<DialogModal closeable :show="show" @close="show = false">
<template #title>
<div class="flex justify-center">
<span> Update Project Billable Rate </span>
</div>
</template>
<template #content>
<div class="flex items-center space-x-4">
<div class="col-span-6 sm:col-span-4 flex-1">
<p class="py-1 text-center">
The billable rate of {{ projectName }} will be updated
to
<strong>{{
newBillableRate
? formatCents(newBillableRate)
: ' the default rate of the organization'
}}</strong
>.
</p>
<p class="py-1 text-center font-semibold max-w-md mx-auto">
Do you want to update all existing time entries, where
the project billable rate applies as well?
</p>
<div class="space-x-3 pt-5 pb-2 flex justify-center">
<PrimaryButton
:class="{ 'opacity-25': saving }"
:disabled="saving"
@click="emit('submit', true)">
Yes, update existing time entries
</PrimaryButton>
<PrimaryButton
:class="{ 'opacity-25': saving }"
:disabled="saving"
@click="emit('submit', false)">
No, only for new time entries
</PrimaryButton>
</div>
<p class="text-center pt-3 pb-1">
Learn more about the
<a
target="_blank"
href="https://docs.solidtime.io/user-guide/billable-rates"
class="text-blue-400 hover:text-blue-500 transition"
>billable rate logic
<ArrowTopRightOnSquareIcon
class="w-4 -mt-0.5 inline-block"></ArrowTopRightOnSquareIcon
></a>
</p>
</div>
</div>
</template>
<template #footer>
<SecondaryButton @click="show = false"> Cancel </SecondaryButton>
</template>
</DialogModal>
</template>
<style scoped></style>

View File

@@ -15,7 +15,7 @@ const model = defineModel<string>({ default: '' });
:style="{
backgroundColor: model,
}"
class="w-5 h-5 rounded-full cursor-pointer"></div>
class="w-6 h-6 rounded-full cursor-pointer"></div>
</button>
</template>
<template #content>

View File

@@ -15,12 +15,13 @@ import ProjectColorSelector from '@/Components/Common/Project/ProjectColorSelect
import ProjectEditBillableSection from '@/Components/Common/Project/ProjectEditBillableSection.vue';
import { UserCircleIcon } from '@heroicons/vue/20/solid';
import InputLabel from '@/Components/InputLabel.vue';
import ProjectBillableRateModal from '@/Components/Common/Project/ProjectBillableRateModal.vue';
const { updateProject } = useProjectsStore();
const { clients } = storeToRefs(useClientsStore());
const show = defineModel('show', { default: false });
const saving = ref(false);
const showBillableRateModal = ref(false);
const props = defineProps<{
originalProject: Project;
}>();
@@ -34,6 +35,10 @@ const project = ref<CreateProjectBody>({
});
async function submit() {
if (props.originalProject.billable_rate !== project.value.billable_rate) {
showBillableRateModal.value = true;
return;
}
await updateProject(props.originalProject.id, project.value);
show.value = false;
}
@@ -50,6 +55,14 @@ const currentClientName = computed(() => {
}
return 'No Client';
});
async function submitBillableRate(billableRateUpdateTimeEntries: boolean) {
project.value.billable_rate_update_time_entries =
billableRateUpdateTimeEntries;
await updateProject(props.originalProject.id, project.value);
show.value = false;
showBillableRateModal.value = false;
}
</script>
<template>
@@ -62,11 +75,12 @@ const currentClientName = computed(() => {
<template #content>
<div
class="sm:flex items-center space-y-2 sm:space-y-0 sm:space-x-4">
class="sm:flex items-center space-y-2 sm:space-y-0 sm:space-x-5">
<div class="flex-1 flex items-center">
<div class="text-center pr-5">
<div class="text-center">
<InputLabel for="color" value="Color" />
<ProjectColorSelector
class="mt-1"
v-model="project.color"></ProjectColorSelector>
</div>
</div>
@@ -85,7 +99,7 @@ const currentClientName = computed(() => {
</div>
<div class="">
<InputLabel for="client" value="Client" />
<ClientDropdown class="mt-2" v-model="project.client_id">
<ClientDropdown class="mt-1" v-model="project.client_id">
<template #trigger>
<Badge
class="bg-input-background cursor-pointer hover:bg-tertiary"
@@ -93,7 +107,7 @@ const currentClientName = computed(() => {
<div class="flex items-center space-x-2">
<UserCircleIcon
class="w-5 text-icon-default"></UserCircleIcon>
<span>
<span class="whitespace-nowrap">
{{ currentClientName }}
</span>
</div>
@@ -121,6 +135,11 @@ const currentClientName = computed(() => {
</PrimaryButton>
</template>
</DialogModal>
<ProjectBillableRateModal
v-model:show="showBillableRateModal"
@submit="submitBillableRate"
:new-billable-rate="project.billable_rate"
:project-name="project.name"></ProjectBillableRateModal>
</template>
<style scoped></style>