Skip to content

Commit

Permalink
add project member role selection to create and update modals, fixes …
Browse files Browse the repository at this point in the history
…ST-446
  • Loading branch information
Onatcer committed Sep 30, 2024
1 parent a6e5d37 commit 6c4256b
Show file tree
Hide file tree
Showing 8 changed files with 113 additions and 25 deletions.
2 changes: 2 additions & 0 deletions app/Http/Resources/V1/ProjectMember/ProjectMemberResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ public function toArray(Request $request): array
'member_id' => $this->resource->member_id,
/** @var string $project_id ID of the project */
'project_id' => $this->resource->project_id,
/** @var string $role Role of the project member */
'role' => $this->resource->role->value,
];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import { useProjectMembersStore } from '@/utils/useProjectMembers';
import MemberCombobox from '@/Components/Common/Member/MemberCombobox.vue';
import BillableRateInput from '@/packages/ui/src/Input/BillableRateInput.vue';
import { getOrganizationCurrencyString } from '@/utils/money';
import { InputLabel } from '@/packages/ui/src';
import ProjectMemberRoleSelect from '@/Components/Common/ProjectMember/ProjectMemberRoleSelect.vue';
const { createProjectMember } = useProjectMembersStore();
const show = defineModel('show', { default: false });
const saving = ref(false);
Expand All @@ -24,6 +26,7 @@ const props = defineProps<{
const projectMember = ref<CreateProjectMemberBody>({
member_id: '',
billable_rate: null,
role: 'normal',
});
async function submit() {
Expand All @@ -32,6 +35,7 @@ async function submit() {
projectMember.value = {
member_id: '',
billable_rate: null,
role: 'normal',
};
}
Expand All @@ -49,20 +53,29 @@ useFocus(projectNameInput, { initialValue: true });
</template>

<template #content>
<div class="grid grid-cols-3 items-center space-x-4">
<div class="col-span-3 sm:col-span-2">
<div class="items-center space-y-4">
<div>
<InputLabel value="Member" class="mb-2"></InputLabel>
<MemberCombobox
:hidden-members="props.existingMembers"
v-model="projectMember.member_id"></MemberCombobox>
</div>
<div class="col-span-3 sm:col-span-1 flex-1">
<div>
<InputLabel
value="Billable Rate"
for="billable_rate"></InputLabel>
<BillableRateInput
name="billable_rate"
:currency="getOrganizationCurrencyString()"
v-model="
projectMember.billable_rate
"></BillableRateInput>
</div>
<div>
<InputLabel value="Role" class="mb-2"></InputLabel>
<ProjectMemberRoleSelect
v-model="projectMember.role"></ProjectMemberRoleSelect>
</div>
</div>
</template>
<template #footer>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,15 @@ import type {
} from '@/packages/api/src';
import PrimaryButton from '@/packages/ui/src/Buttons/PrimaryButton.vue';
import { useFocus } from '@vueuse/core';
import { useProjectMembersStore } from '@/utils/useProjectMembers';
import {
type ProjectMemberRole,
useProjectMembersStore,
} from '@/utils/useProjectMembers';
import BillableRateInput from '@/packages/ui/src/Input/BillableRateInput.vue';
import { UserIcon } from '@heroicons/vue/24/solid';
import ProjectMemberBillableRateModal from '@/Components/Common/ProjectMember/ProjectMemberBillableRateModal.vue';
import InputLabel from '@/packages/ui/src/Input/InputLabel.vue';
import { getOrganizationCurrencyString } from '@/utils/money';
import ProjectMemberRoleSelect from '@/Components/Common/ProjectMember/ProjectMemberRoleSelect.vue';
const { updateProjectMember } = useProjectMembersStore();
const show = defineModel('show', { default: false });
Expand All @@ -26,6 +29,7 @@ const props = defineProps<{
const projectMemberBody = ref<UpdateProjectMemberBody>({
billable_rate: props.projectMember.billable_rate,
role: props.projectMember.role as ProjectMemberRole,
});
const showBillableRateModal = ref(false);
async function submit() {
Expand All @@ -40,6 +44,7 @@ async function submit() {
show.value = false;
projectMemberBody.value = {
billable_rate: null,
role: 'normal',
};
}
Expand All @@ -55,6 +60,7 @@ watch(
if (value) {
projectMemberBody.value = {
billable_rate: props.projectMember.billable_rate,
role: props.projectMember.role as ProjectMemberRole,
};
}
}
Expand All @@ -69,7 +75,7 @@ useFocus(projectNameInput, { initialValue: true });
<DialogModal closeable :show="show" @close="show = false">
<template #title>
<div class="flex space-x-2">
<span>Edit Project Member</span>
<span>Edit Project Member "{{ props.name }}"</span>
</div>
</template>

Expand All @@ -80,23 +86,26 @@ useFocus(projectNameInput, { initialValue: true });
:new-billable-rate="projectMemberBody.billable_rate"
@close="showBillableRateModal = false"
@submit="submitBillableRate"></ProjectMemberBillableRateModal>
<div class="grid grid-cols-3 items-center space-x-4">
<div
class="col-span-3 sm:col-span-2 space-x-2 flex items-center">
<UserIcon class="w-4 text-muted"></UserIcon>
<span>{{ props.name }}</span>
</div>
<div class="col-span-3 sm:col-span-1 flex-1">
<InputLabel
for="billable_rate"
value="Billable Rate"></InputLabel>
<BillableRateInput
@keydown.enter="submit"
:currency="getOrganizationCurrencyString()"
name="billable_rate"
v-model="
projectMemberBody.billable_rate
"></BillableRateInput>
<div>
<div class="items-center space-y-4">
<div>
<InputLabel
value="Billable Rate"
for="billable_rate"></InputLabel>
<BillableRateInput
name="billable_rate"
:currency="getOrganizationCurrencyString()"
v-model="
projectMemberBody.billable_rate
"></BillableRateInput>
</div>
<div>
<InputLabel value="Role" class="mb-2"></InputLabel>
<ProjectMemberRoleSelect
v-model="
projectMemberBody.role
"></ProjectMemberRoleSelect>
</div>
</div>
</div>
</template>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<script setup lang="ts">
import SelectDropdown from '@/packages/ui/src/Input/SelectDropdown.vue';
import Badge from '@/packages/ui/src/Badge.vue';
import { ChevronDownIcon } from '@heroicons/vue/20/solid';
import type { ProjectMemberRole } from '@/utils/useProjectMembers';
type ProjectMemberRoleItem = { key: ProjectMemberRole; name: string };
const projectMemberRoles: ProjectMemberRoleItem[] = [
{
key: 'normal',
name: 'Normal',
},
{
key: 'manager',
name: 'Manager',
},
];
const model = defineModel<string>({
default: 'normal',
});
function getKeyFromItem(item: ProjectMemberRoleItem) {
return item.key;
}
function getNameFromItem(item: ProjectMemberRoleItem) {
return item.name;
}
function getNameForKey(key: string | undefined) {
return projectMemberRoles.find((item) => item.key === key)?.name ?? '';
}
</script>

<template>
<SelectDropdown
v-model="model"
:get-key-from-item="getKeyFromItem"
:get-name-for-item="getNameFromItem"
:items="projectMemberRoles">
<template #trigger>
<Badge size="xlarge" class="bg-input-background cursor-pointer">
<span>
{{ getNameForKey(model) }}
</span>
<ChevronDownIcon class="text-muted w-5"></ChevronDownIcon>
</Badge>
</template>
</SelectDropdown>
</template>

<style scoped></style>
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ const showEditModal = ref(false);
}}
</div>
<div class="whitespace-nowrap px-3 py-4 text-sm text-muted">
{{ capitalizeFirstLetter(member?.role ?? '') }}
{{ capitalizeFirstLetter(projectMember?.role ?? '') }}
</div>
<div
class="relative whitespace-nowrap flex items-center pl-3 text-right text-sm font-medium sm:pr-0 pr-4 sm:pr-6 lg:pr-8 3xl:pr-12">
Expand Down
10 changes: 9 additions & 1 deletion resources/js/packages/api/src/openapi.json.client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ const ProjectResource = z
is_billable: z.boolean(),
estimated_time: z.union([z.number(), z.null()]),
spent_time: z.number().int(),
limited_visibility: z.boolean(),
})
.passthrough();
const ProjectStoreRequest = z
Expand Down Expand Up @@ -99,16 +100,22 @@ const ProjectMemberResource = z
billable_rate: z.union([z.number(), z.null()]),
member_id: z.string(),
project_id: z.string(),
role: z.string(),
})
.passthrough();
const ProjectMemberRole = z.enum(['manager', 'normal']);
const ProjectMemberStoreRequest = z
.object({
member_id: z.string().uuid(),
billable_rate: z.union([z.number(), z.null()]).optional(),
role: ProjectMemberRole,
})
.passthrough();
const ProjectMemberUpdateRequest = z
.object({ billable_rate: z.union([z.number(), z.null()]) })
.object({
billable_rate: z.union([z.number(), z.null()]),
role: ProjectMemberRole,
})
.partial()
.passthrough();
const TagResource = z
Expand Down Expand Up @@ -257,6 +264,7 @@ export const schemas = {
ProjectStoreRequest,
ProjectUpdateRequest,
ProjectMemberResource,
ProjectMemberRole,
ProjectMemberStoreRequest,
ProjectMemberUpdateRequest,
TagResource,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ watchEffect(() => {
tasks: [],
estimated_time: null,
spent_time: 0,
limited_visibility: false,
},
],
});
Expand Down
1 change: 1 addition & 0 deletions resources/js/utils/useProjectMembers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type {
} from '@/packages/api/src';
import { getCurrentOrganizationId } from '@/utils/useUser';
import { useNotificationsStore } from '@/utils/notification';
export type ProjectMemberRole = 'normal' | 'manager';

export const useProjectMembersStore = defineStore('project-members', () => {
const projectMemberResponse = ref<ProjectMemberResponse | null>(null);
Expand Down

0 comments on commit 6c4256b

Please sign in to comment.