mirror of
https://github.com/solidtime-io/solidtime.git
synced 2026-08-16 12:12:15 +01:00
change project member id from member_id to user_id, fix member combobox
This commit is contained in:
@@ -1,6 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Dropdown from '@/Components/Dropdown.vue';
|
import { computed, nextTick, onMounted, ref, watch } from 'vue';
|
||||||
import { type Component, computed, nextTick, onMounted, ref, watch } from 'vue';
|
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import ClientDropdownItem from '@/Components/Common/Client/ClientDropdownItem.vue';
|
import ClientDropdownItem from '@/Components/Common/Client/ClientDropdownItem.vue';
|
||||||
import { useMembersStore } from '@/utils/useMembers';
|
import { useMembersStore } from '@/utils/useMembers';
|
||||||
@@ -8,6 +7,7 @@ import { UserIcon, XMarkIcon } from '@heroicons/vue/24/solid';
|
|||||||
import TextInput from '@/Components/TextInput.vue';
|
import TextInput from '@/Components/TextInput.vue';
|
||||||
import { useFocus } from '@vueuse/core';
|
import { useFocus } from '@vueuse/core';
|
||||||
import type { ProjectMember } from '@/utils/api';
|
import type { ProjectMember } from '@/utils/api';
|
||||||
|
import Dropdown from '@/Components/Dropdown.vue';
|
||||||
|
|
||||||
const membersStore = useMembersStore();
|
const membersStore = useMembersStore();
|
||||||
const { members } = storeToRefs(membersStore);
|
const { members } = storeToRefs(membersStore);
|
||||||
@@ -21,7 +21,6 @@ const props = defineProps<{
|
|||||||
}>();
|
}>();
|
||||||
|
|
||||||
const searchInput = ref<HTMLInputElement | null>(null);
|
const searchInput = ref<HTMLInputElement | null>(null);
|
||||||
const dropdownViewport = ref<Component | null>(null);
|
|
||||||
|
|
||||||
const searchValue = ref('');
|
const searchValue = ref('');
|
||||||
|
|
||||||
@@ -38,7 +37,7 @@ const filteredMembers = computed(() => {
|
|||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.includes(searchValue.value?.toLowerCase()?.trim() || '') &&
|
.includes(searchValue.value?.toLowerCase()?.trim() || '') &&
|
||||||
!props.hiddenMembers.some(
|
!props.hiddenMembers.some(
|
||||||
(hiddenMember) => hiddenMember.user_id === member.id
|
(hiddenMember) => hiddenMember.user_id === member.user_id
|
||||||
) &&
|
) &&
|
||||||
member.is_placeholder === false
|
member.is_placeholder === false
|
||||||
);
|
);
|
||||||
@@ -55,7 +54,7 @@ onMounted(() => {
|
|||||||
|
|
||||||
function resetHighlightedItem() {
|
function resetHighlightedItem() {
|
||||||
if (filteredMembers.value.length > 0) {
|
if (filteredMembers.value.length > 0) {
|
||||||
highlightedItemId.value = filteredMembers.value[0].id;
|
highlightedItemId.value = filteredMembers.value[0].user_id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,10 +65,10 @@ function updateSearchValue(event: Event) {
|
|||||||
const highlightedClientId = highlightedItemId.value;
|
const highlightedClientId = highlightedItemId.value;
|
||||||
if (highlightedClientId) {
|
if (highlightedClientId) {
|
||||||
const highlightedClient = members.value.find(
|
const highlightedClient = members.value.find(
|
||||||
(member) => member.id === highlightedClientId
|
(member) => member.user_id === highlightedClientId
|
||||||
);
|
);
|
||||||
if (highlightedClient) {
|
if (highlightedClient) {
|
||||||
model.value = highlightedClient.id;
|
model.value = highlightedClient.user_id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -80,6 +79,7 @@ function updateSearchValue(event: Event) {
|
|||||||
const emit = defineEmits(['update:modelValue', 'changed']);
|
const emit = defineEmits(['update:modelValue', 'changed']);
|
||||||
|
|
||||||
function updateMember(newValue: string | null) {
|
function updateMember(newValue: string | null) {
|
||||||
|
console.log(newValue);
|
||||||
if (newValue) {
|
if (newValue) {
|
||||||
model.value = newValue;
|
model.value = newValue;
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
@@ -95,10 +95,10 @@ function moveHighlightUp() {
|
|||||||
);
|
);
|
||||||
if (currentHightlightedIndex === 0) {
|
if (currentHightlightedIndex === 0) {
|
||||||
highlightedItemId.value =
|
highlightedItemId.value =
|
||||||
filteredMembers.value[filteredMembers.value.length - 1].id;
|
filteredMembers.value[filteredMembers.value.length - 1].user_id;
|
||||||
} else {
|
} else {
|
||||||
highlightedItemId.value =
|
highlightedItemId.value =
|
||||||
filteredMembers.value[currentHightlightedIndex - 1].id;
|
filteredMembers.value[currentHightlightedIndex - 1].user_id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -109,10 +109,10 @@ function moveHighlightDown() {
|
|||||||
highlightedItem.value
|
highlightedItem.value
|
||||||
);
|
);
|
||||||
if (currentHightlightedIndex === filteredMembers.value.length - 1) {
|
if (currentHightlightedIndex === filteredMembers.value.length - 1) {
|
||||||
highlightedItemId.value = filteredMembers.value[0].id;
|
highlightedItemId.value = filteredMembers.value[0].user_id;
|
||||||
} else {
|
} else {
|
||||||
highlightedItemId.value =
|
highlightedItemId.value =
|
||||||
filteredMembers.value[currentHightlightedIndex + 1].id;
|
filteredMembers.value[currentHightlightedIndex + 1].user_id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -120,13 +120,14 @@ function moveHighlightDown() {
|
|||||||
const highlightedItemId = ref<string | null>(null);
|
const highlightedItemId = ref<string | null>(null);
|
||||||
const highlightedItem = computed(() => {
|
const highlightedItem = computed(() => {
|
||||||
return members.value.find(
|
return members.value.find(
|
||||||
(member) => member.id === highlightedItemId.value
|
(member) => member.user_id === highlightedItemId.value
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const currentValue = computed(() => {
|
const currentValue = computed(() => {
|
||||||
if (model.value) {
|
if (model.value) {
|
||||||
return members.value.find((member) => member.id === model.value)?.name;
|
return members.value.find((member) => member.user_id === model.value)
|
||||||
|
?.name;
|
||||||
}
|
}
|
||||||
return searchValue.value;
|
return searchValue.value;
|
||||||
});
|
});
|
||||||
@@ -134,11 +135,23 @@ const currentValue = computed(() => {
|
|||||||
const hasMemberSelected = computed(() => {
|
const hasMemberSelected = computed(() => {
|
||||||
return model.value !== '';
|
return model.value !== '';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const showMembersDropdown = ref(true);
|
||||||
|
|
||||||
|
function onUnfocus() {
|
||||||
|
// TODO this is a hack to prevent the dropdown from closing when clicking on the dropdown
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!focused.value) {
|
||||||
|
showMembersDropdown.value = false;
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="flex relative">
|
<div class="flex relative">
|
||||||
<div
|
<div
|
||||||
|
ref="reference"
|
||||||
class="absolute h-full items-center px-3 w-full flex justify-between">
|
class="absolute h-full items-center px-3 w-full flex justify-between">
|
||||||
<UserIcon class="relative z-10 w-4 text-muted"></UserIcon>
|
<UserIcon class="relative z-10 w-4 text-muted"></UserIcon>
|
||||||
<button
|
<button
|
||||||
@@ -156,32 +169,37 @@ const hasMemberSelected = computed(() => {
|
|||||||
@keydown.up.prevent="moveHighlightUp"
|
@keydown.up.prevent="moveHighlightUp"
|
||||||
class="relative w-full pl-10"
|
class="relative w-full pl-10"
|
||||||
@keydown.down.prevent="moveHighlightDown"
|
@keydown.down.prevent="moveHighlightDown"
|
||||||
|
@focusin="showMembersDropdown = true"
|
||||||
|
@blur="onUnfocus"
|
||||||
placeholder="Search for a member..."
|
placeholder="Search for a member..."
|
||||||
ref="searchInput" />
|
ref="searchInput" />
|
||||||
</div>
|
</div>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
align="left"
|
align="bottom-start"
|
||||||
width="300"
|
width="300"
|
||||||
v-model="focused"
|
v-model="showMembersDropdown"
|
||||||
:closeOnContentClick="true">
|
:closeOnContentClick="true">
|
||||||
<template #content>
|
<template #content>
|
||||||
<div ref="dropdownViewport" class="w-60">
|
<div
|
||||||
<div
|
class="py-2 text-white px-3"
|
||||||
v-for="member in filteredMembers"
|
v-if="filteredMembers.length === 0">
|
||||||
:key="member.id"
|
All members are already added.
|
||||||
role="option"
|
</div>
|
||||||
:value="member.id"
|
<div
|
||||||
:class="{
|
v-for="member in filteredMembers"
|
||||||
'bg-card-background-active':
|
:key="member.user_id"
|
||||||
member.id === highlightedItemId,
|
role="option"
|
||||||
}"
|
:value="member.user_id"
|
||||||
@click="updateMember(member.id)"
|
:class="{
|
||||||
data-testid="client_dropdown_entries"
|
'bg-card-background-active':
|
||||||
:data-client-id="member.id">
|
member.user_id === highlightedItemId,
|
||||||
<ClientDropdownItem
|
}"
|
||||||
:selected="isMemberSelected(member.id)"
|
@click="updateMember(member.user_id)"
|
||||||
:name="member.name"></ClientDropdownItem>
|
data-testid="client_dropdown_entries"
|
||||||
</div>
|
:data-client-id="member.user_id">
|
||||||
|
<ClientDropdownItem
|
||||||
|
:selected="isMemberSelected(member.user_id)"
|
||||||
|
:name="member.name"></ClientDropdownItem>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
|
|||||||
@@ -25,6 +25,10 @@ const projectMember = ref<CreateProjectMemberBody>({
|
|||||||
async function submit() {
|
async function submit() {
|
||||||
await createProjectMember(props.projectId, projectMember.value);
|
await createProjectMember(props.projectId, projectMember.value);
|
||||||
show.value = false;
|
show.value = false;
|
||||||
|
projectMember.value = {
|
||||||
|
user_id: '',
|
||||||
|
billable_rate: null,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const projectNameInput = ref<HTMLInputElement | null>(null);
|
const projectNameInput = ref<HTMLInputElement | null>(null);
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ function deleteProjectMember() {
|
|||||||
const { members } = storeToRefs(useMembersStore());
|
const { members } = storeToRefs(useMembersStore());
|
||||||
const member = computed(() => {
|
const member = computed(() => {
|
||||||
return members.value.find(
|
return members.value.find(
|
||||||
(member) => member.id === props.projectMember.user_id
|
(member) => member.user_id === props.projectMember.user_id
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user