mirror of
https://github.com/solidtime-io/solidtime.git
synced 2026-08-17 20:52:14 +01:00
refactor to shadcn components, dynamically load extension frontend
add jetstream permissions, add dynamic inertia module loading, add shadcn components, change modals and dropdowns to shadcn dismissable layer,
This commit is contained in:
@@ -49,7 +49,7 @@ function getNameForKey(key: BillableKey | undefined) {
|
||||
<span>
|
||||
{{ getNameForKey(model) }}
|
||||
</span>
|
||||
<ChevronDownIcon class="text-muted w-5"></ChevronDownIcon>
|
||||
<ChevronDownIcon class="text-text-secondary w-5"></ChevronDownIcon>
|
||||
</Badge>
|
||||
</template>
|
||||
</SelectDropdown>
|
||||
|
||||
@@ -64,12 +64,12 @@ const currentValue = computed(() => {
|
||||
<Badge
|
||||
tag="button"
|
||||
class="flex w-full text-base text-left space-x-3 px-3 text-text-secondary bg-input-background font-normal cursor py-1.5">
|
||||
<UserIcon class="relative z-10 w-4 text-muted"></UserIcon>
|
||||
<UserIcon class="relative z-10 w-4 text-text-secondary"></UserIcon>
|
||||
<div v-if="currentValue" class="flex-1 truncate">
|
||||
{{ currentValue }}
|
||||
</div>
|
||||
<div v-else class="flex-1">Select a member...</div>
|
||||
<ChevronDownIcon class="w-4 text-muted"></ChevronDownIcon>
|
||||
<ChevronDownIcon class="w-4 text-text-secondary"></ChevronDownIcon>
|
||||
</Badge>
|
||||
</template>
|
||||
</SelectDropdown>
|
||||
|
||||
@@ -194,7 +194,7 @@ useFocus(clientNameInput, { initialValue: true });
|
||||
</div>
|
||||
|
||||
<!-- Role Description -->
|
||||
<div class="mt-2 text-xs text-muted text-start">
|
||||
<div class="mt-2 text-xs text-text-secondary text-start">
|
||||
{{ role.description }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -76,7 +76,7 @@ async function submit() {
|
||||
<div class="py-5 flex flex-col md:flex-row gap-6 items-center">
|
||||
<div class="flex-1">
|
||||
<Badge class="flex w-full text-base text-left space-x-3 px-3 text-text-secondary font-normal cursor py-1.5">
|
||||
<UserIcon class="relative z-10 w-4 text-muted"></UserIcon>
|
||||
<UserIcon class="relative z-10 w-4 text-text-secondary"></UserIcon>
|
||||
<div class="flex-1 font-medium truncate">
|
||||
{{ member.name }}
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,12 @@
|
||||
import { TrashIcon, UserCircleIcon, PencilSquareIcon, ArrowDownOnSquareStackIcon } from '@heroicons/vue/20/solid';
|
||||
import type { Member } from '@/packages/api/src';
|
||||
import {canDeleteMembers, canMakeMembersPlaceholders, canMergeMembers, canUpdateMembers} from '@/utils/permissions';
|
||||
import MoreOptionsDropdown from '@/packages/ui/src/MoreOptionsDropdown.vue';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/Components/ui/dropdown-menu';
|
||||
|
||||
const emit = defineEmits<{
|
||||
delete: [];
|
||||
@@ -13,51 +18,65 @@ const emit = defineEmits<{
|
||||
const props = defineProps<{
|
||||
member: Member;
|
||||
}>();
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<MoreOptionsDropdown
|
||||
v-if="canUpdateMembers() || canDeleteMembers()"
|
||||
:label="'Actions for Member ' + props.member.name">
|
||||
<div class="min-w-[150px]">
|
||||
<DropdownMenu v-if="canUpdateMembers() || canDeleteMembers()">
|
||||
<DropdownMenuTrigger as-child>
|
||||
<button
|
||||
class="focus-visible:outline-none focus-visible:bg-card-background rounded-full focus-visible:ring-2 focus-visible:ring-ring focus-visible:opacity-100 hover:bg-card-background group-hover:opacity-100 opacity-20 transition-opacity text-text-secondary"
|
||||
:aria-label="'Actions for Member ' + props.member.name">
|
||||
<svg
|
||||
class="h-8 w-8 p-1 rounded-full"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="1.5"
|
||||
d="M12 5.92A.96.96 0 1 0 12 4a.96.96 0 0 0 0 1.92m0 7.04a.96.96 0 1 0 0-1.92a.96.96 0 0 0 0 1.92M12 20a.96.96 0 1 0 0-1.92a.96.96 0 0 0 0 1.92" />
|
||||
</svg>
|
||||
</button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent class="min-w-[150px]" align="end">
|
||||
<DropdownMenuItem
|
||||
v-if="canUpdateMembers()"
|
||||
:aria-label="'Edit Member ' + props.member.name"
|
||||
class="flex items-center space-x-3 w-full px-3 py-2.5 text-start text-sm font-medium leading-5 text-text-primary hover:bg-card-background-active focus:outline-none focus:bg-card-background-active transition duration-150 ease-in-out"
|
||||
class="flex items-center space-x-3 cursor-pointer"
|
||||
@click="emit('edit')">
|
||||
<PencilSquareIcon
|
||||
class="w-5 text-icon-active"></PencilSquareIcon>
|
||||
<PencilSquareIcon class="w-5 text-icon-active" />
|
||||
<span>Edit</span>
|
||||
</button>
|
||||
<button
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
v-if="canDeleteMembers()"
|
||||
:aria-label="'Delete Member ' + props.member.name"
|
||||
data-testid="member_delete"
|
||||
class="flex items-center space-x-3 w-full px-3 py-2.5 text-start text-sm font-medium leading-5 text-text-primary hover:bg-card-background-active focus:outline-none focus:bg-card-background-active transition duration-150 ease-in-out"
|
||||
class="flex items-center space-x-3 cursor-pointer text-destructive focus:text-destructive"
|
||||
@click="emit('delete')">
|
||||
<TrashIcon class="w-5 text-icon-active"></TrashIcon>
|
||||
<TrashIcon class="w-5" />
|
||||
<span>Delete</span>
|
||||
</button>
|
||||
<button
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
v-if="props.member.role === 'placeholder' && canMergeMembers()"
|
||||
:aria-label="'Merge Member ' + props.member.name"
|
||||
data-testid="member_merge"
|
||||
class="flex items-center space-x-3 w-full px-3 py-2.5 text-start text-sm font-medium leading-5 text-text-primary hover:bg-card-background-active focus:outline-none focus:bg-card-background-active transition duration-150 ease-in-out"
|
||||
class="flex items-center space-x-3 cursor-pointer"
|
||||
@click="emit('merge')">
|
||||
<ArrowDownOnSquareStackIcon class="w-5 text-icon-active"></ArrowDownOnSquareStackIcon>
|
||||
<ArrowDownOnSquareStackIcon class="w-5 text-icon-active" />
|
||||
<span>Merge</span>
|
||||
</button>
|
||||
<button
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
v-if="props.member.role !== 'placeholder' && canMakeMembersPlaceholders()"
|
||||
:aria-label="'Make Member ' + props.member.name + ' a placeholder'"
|
||||
class="flex items-center space-x-3 w-full px-3 py-2.5 text-start text-sm font-medium leading-5 text-text-primary hover:bg-card-background-active focus:outline-none focus:bg-card-background-active transition duration-150 ease-in-out"
|
||||
class="flex items-center space-x-3 cursor-pointer"
|
||||
@click="emit('makePlaceholder')">
|
||||
<UserCircleIcon class="w-5 text-icon-active"></UserCircleIcon>
|
||||
<UserCircleIcon class="w-5 text-icon-active" />
|
||||
<span>Deactivate</span>
|
||||
</button>
|
||||
</div>
|
||||
</MoreOptionsDropdown>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
|
||||
@@ -43,7 +43,7 @@ function getNameForKey(key: string | undefined) {
|
||||
<span>
|
||||
{{ getNameForKey(model) }}
|
||||
</span>
|
||||
<ChevronDownIcon class="text-muted w-5"></ChevronDownIcon>
|
||||
<ChevronDownIcon class="text-text-secondary w-5"></ChevronDownIcon>
|
||||
</Badge>
|
||||
</template>
|
||||
</SelectDropdown>
|
||||
|
||||
@@ -64,13 +64,13 @@ const userHasValidMailAddress = computed(() => {
|
||||
{{ member.name }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="whitespace-nowrap px-3 py-4 text-sm text-muted">
|
||||
<div class="whitespace-nowrap px-3 py-4 text-sm text-text-secondary">
|
||||
{{ member.email }}
|
||||
</div>
|
||||
<div class="whitespace-nowrap px-3 py-4 text-sm text-muted">
|
||||
<div class="whitespace-nowrap px-3 py-4 text-sm text-text-secondary">
|
||||
{{ capitalizeFirstLetter(member.role) }}
|
||||
</div>
|
||||
<div class="whitespace-nowrap px-3 py-4 text-sm text-muted">
|
||||
<div class="whitespace-nowrap px-3 py-4 text-sm text-text-secondary">
|
||||
{{
|
||||
member.billable_rate
|
||||
? formatCents(
|
||||
@@ -81,7 +81,7 @@ const userHasValidMailAddress = computed(() => {
|
||||
}}
|
||||
</div>
|
||||
<div
|
||||
class="whitespace-nowrap px-3 py-4 text-sm text-muted flex space-x-1 items-center font-medium">
|
||||
class="whitespace-nowrap px-3 py-4 text-sm text-text-secondary flex space-x-1 items-center font-medium">
|
||||
<CheckCircleIcon
|
||||
v-if="member.is_placeholder === false"
|
||||
class="w-5"></CheckCircleIcon>
|
||||
|
||||
Reference in New Issue
Block a user