mirror of
https://github.com/solidtime-io/solidtime.git
synced 2026-08-16 20:22:15 +01:00
update dependencies, update eslint config, update optional ts props types
This commit is contained in:
@@ -112,7 +112,7 @@ const showBlackFridayBanner = computed(() => {
|
||||
<span>Upgrade now</span>
|
||||
</div>
|
||||
</Link>
|
||||
<button @click="hideBlackFridayBanner = true" class="p-1">
|
||||
<button class="p-1" @click="hideBlackFridayBanner = true">
|
||||
<XMarkIcon
|
||||
class="w-4 opacity-50 hover:opacity-100"></XMarkIcon>
|
||||
</button>
|
||||
@@ -142,7 +142,7 @@ const showBlackFridayBanner = computed(() => {
|
||||
<span>Upgrade now</span>
|
||||
</div>
|
||||
</Link>
|
||||
<button @click="hideTrialBanner = true" class="p-1">
|
||||
<button class="p-1" @click="hideTrialBanner = true">
|
||||
<XMarkIcon
|
||||
class="w-4 opacity-50 hover:opacity-100"></XMarkIcon>
|
||||
</button>
|
||||
@@ -174,7 +174,7 @@ const showBlackFridayBanner = computed(() => {
|
||||
<span>Upgrade now</span>
|
||||
</div>
|
||||
</Link>
|
||||
<button @click="hideBlockedBanner = true" class="p-1">
|
||||
<button class="p-1" @click="hideBlockedBanner = true">
|
||||
<XMarkIcon
|
||||
class="w-4 opacity-50 hover:opacity-100"></XMarkIcon>
|
||||
</button>
|
||||
@@ -206,7 +206,7 @@ const showBlackFridayBanner = computed(() => {
|
||||
<span>Upgrade now</span>
|
||||
</div>
|
||||
</Link>
|
||||
<button @click="hideFreeUpgradeBanner = true" class="p-1">
|
||||
<button class="p-1" @click="hideFreeUpgradeBanner = true">
|
||||
<XMarkIcon
|
||||
class="w-4 opacity-50 hover:opacity-100"></XMarkIcon>
|
||||
</button>
|
||||
|
||||
@@ -45,10 +45,10 @@ useFocus(clientNameInput, { initialValue: true });
|
||||
v-model="client.name"
|
||||
type="text"
|
||||
placeholder="Client Name"
|
||||
@keydown.enter="submit"
|
||||
class="mt-1 block w-full"
|
||||
required
|
||||
autocomplete="clientName" />
|
||||
autocomplete="clientName"
|
||||
@keydown.enter="submit" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -46,10 +46,10 @@ useFocus(clientNameInput, { initialValue: true });
|
||||
v-model="clientBody.name"
|
||||
type="text"
|
||||
placeholder="Client Name"
|
||||
@keydown.enter="submit"
|
||||
class="mt-1 block w-full"
|
||||
required
|
||||
autocomplete="clientName" />
|
||||
autocomplete="clientName"
|
||||
@keydown.enter="submit" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -23,28 +23,28 @@ const props = defineProps<{
|
||||
<div class="min-w-[150px]">
|
||||
<button
|
||||
v-if="canUpdateClients()"
|
||||
@click="emit('edit')"
|
||||
:aria-label="'Edit Client ' + props.client.name"
|
||||
data-testid="client_edit"
|
||||
class="flex items-center space-x-3 w-full px-3 py-2.5 text-start text-sm font-medium leading-5 text-white 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 w-full px-3 py-2.5 text-start text-sm font-medium leading-5 text-white hover:bg-card-background-active focus:outline-none focus:bg-card-background-active transition duration-150 ease-in-out"
|
||||
@click="emit('edit')">
|
||||
<PencilSquareIcon
|
||||
class="w-5 text-icon-active"></PencilSquareIcon>
|
||||
<span>Edit</span>
|
||||
</button>
|
||||
<button
|
||||
@click.prevent="emit('archive')"
|
||||
v-if="canUpdateClients()"
|
||||
:aria-label="'Archive Client ' + props.client.name"
|
||||
class="flex items-center space-x-3 w-full px-3 py-2.5 text-start text-sm font-medium leading-5 text-white 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 w-full px-3 py-2.5 text-start text-sm font-medium leading-5 text-white hover:bg-card-background-active focus:outline-none focus:bg-card-background-active transition duration-150 ease-in-out"
|
||||
@click.prevent="emit('archive')">
|
||||
<ArchiveBoxIcon class="w-5 text-icon-active"></ArchiveBoxIcon>
|
||||
<span>{{ client.is_archived ? 'Unarchive' : 'Archive' }}</span>
|
||||
</button>
|
||||
<button
|
||||
v-if="canDeleteClients()"
|
||||
@click="emit('delete')"
|
||||
:aria-label="'Delete Client ' + props.client.name"
|
||||
data-testid="client_delete"
|
||||
class="flex items-center space-x-3 w-full px-3 py-2.5 text-start text-sm font-medium leading-5 text-white 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 w-full px-3 py-2.5 text-start text-sm font-medium leading-5 text-white hover:bg-card-background-active focus:outline-none focus:bg-card-background-active transition duration-150 ease-in-out"
|
||||
@click="emit('delete')">
|
||||
<TrashIcon class="w-5 text-icon-active"></TrashIcon>
|
||||
<span>Delete</span>
|
||||
</button>
|
||||
|
||||
@@ -25,18 +25,18 @@ const createClient = ref(false);
|
||||
style="grid-template-columns: 1fr 150px 200px 80px">
|
||||
<ClientTableHeading></ClientTableHeading>
|
||||
<div
|
||||
class="col-span-2 py-24 text-center"
|
||||
v-if="clients.length === 0">
|
||||
v-if="clients.length === 0"
|
||||
class="col-span-2 py-24 text-center">
|
||||
<UserCircleIcon
|
||||
class="w-8 text-icon-default inline pb-2"></UserCircleIcon>
|
||||
<h3 class="text-white font-semibold">No clients found</h3>
|
||||
<p class="pb-5" v-if="canCreateClients()">
|
||||
<p v-if="canCreateClients()" class="pb-5">
|
||||
Create your first client now!
|
||||
</p>
|
||||
<SecondaryButton
|
||||
v-if="canCreateClients()"
|
||||
@click="createClient = true"
|
||||
:icon="PlusIcon as Component"
|
||||
@click="createClient = true"
|
||||
>Create your First Client
|
||||
</SecondaryButton>
|
||||
</div>
|
||||
|
||||
@@ -38,8 +38,8 @@ const showEditModal = ref(false);
|
||||
<template>
|
||||
<TableRow>
|
||||
<ClientEditModal
|
||||
:client="client"
|
||||
v-model:show="showEditModal"></ClientEditModal>
|
||||
v-model:show="showEditModal"
|
||||
:client="client"></ClientEditModal>
|
||||
<div
|
||||
class="whitespace-nowrap flex items-center space-x-5 3xl:pl-12 py-4 pr-3 text-sm font-medium text-white pl-4 sm:pl-6 lg:pl-8 3xl:pl-12">
|
||||
<span>
|
||||
|
||||
@@ -10,16 +10,16 @@ const emit = defineEmits<{
|
||||
<template>
|
||||
<MoreOptionsDropdown label="Actions for the invitation">
|
||||
<button
|
||||
@click="emit('resend')"
|
||||
data-testid="invitation_delete"
|
||||
class="flex items-center space-x-3 w-full px-3 py-2.5 text-start text-sm font-medium leading-5 text-white 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 w-full px-3 py-2.5 text-start text-sm font-medium leading-5 text-white hover:bg-card-background-active focus:outline-none focus:bg-card-background-active transition duration-150 ease-in-out"
|
||||
@click="emit('resend')">
|
||||
<ArrowPathIcon class="w-5 text-icon-active"></ArrowPathIcon>
|
||||
<span>Resend Invitation</span>
|
||||
</button>
|
||||
<button
|
||||
@click="emit('delete')"
|
||||
data-testid="invitation_delete"
|
||||
class="flex items-center space-x-3 w-full px-3 py-2.5 text-start text-sm font-medium leading-5 text-white 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 w-full px-3 py-2.5 text-start text-sm font-medium leading-5 text-white hover:bg-card-background-active focus:outline-none focus:bg-card-background-active transition duration-150 ease-in-out"
|
||||
@click="emit('delete')">
|
||||
<TrashIcon class="w-5 text-icon-active"></TrashIcon>
|
||||
<span>Delete</span>
|
||||
</button>
|
||||
|
||||
@@ -18,10 +18,10 @@ defineEmits<{
|
||||
|
||||
<template>
|
||||
<BillableRateModal
|
||||
@submit="$emit('submit')"
|
||||
v-model:show="show"
|
||||
v-model:saving="saving"
|
||||
title="Update Member Billable Rate">
|
||||
title="Update Member Billable Rate"
|
||||
@submit="$emit('submit')">
|
||||
<p class="py-1 text-center">
|
||||
The billable rate of {{ memberName }} will be updated to
|
||||
<strong>{{
|
||||
|
||||
@@ -17,8 +17,8 @@ const model = defineModel<string>({
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
hiddenMembers: ProjectMember[];
|
||||
disabled: boolean;
|
||||
hiddenMembers?: ProjectMember[];
|
||||
disabled?: boolean;
|
||||
}>(),
|
||||
{
|
||||
hiddenMembers: () => [] as ProjectMember[],
|
||||
@@ -76,7 +76,7 @@ const currentValue = computed(() => {
|
||||
:items="filteredMembers"
|
||||
:get-key-from-item="(member) => member.id"
|
||||
:get-name-for-item="(member) => member.name">
|
||||
<template v-slot:trigger>
|
||||
<template #trigger>
|
||||
<Badge
|
||||
tag="button"
|
||||
class="flex w-full text-base text-left space-x-3 px-3 text-text-secondary font-normal cursor py-1.5">
|
||||
@@ -84,7 +84,7 @@ const currentValue = computed(() => {
|
||||
<div v-if="currentValue" class="flex-1 truncate">
|
||||
{{ currentValue }}
|
||||
</div>
|
||||
<div class="flex-1" v-else>Select a member...</div>
|
||||
<div v-else class="flex-1">Select a member...</div>
|
||||
<ChevronDownIcon class="w-4 text-muted"></ChevronDownIcon>
|
||||
</Badge>
|
||||
</template>
|
||||
|
||||
@@ -108,11 +108,11 @@ const roleDescription = computed(() => {
|
||||
v-model:saving="saving"
|
||||
v-model:show="showBillableRateModal"
|
||||
:member-name="member.name"
|
||||
:newBillableRate="memberBody.billable_rate"
|
||||
:new-billable-rate="memberBody.billable_rate"
|
||||
@submit="submitBillableRate"></MemberBillableRateModal>
|
||||
<MemberOwnershipTransferConfirmModal
|
||||
:member-name="member.name"
|
||||
v-model:show="showOwnershipTransferConfirmModal"
|
||||
:member-name="member.name"
|
||||
@submit="submit"></MemberOwnershipTransferConfirmModal>
|
||||
<DialogModal closeable :show="show" @close="show = false">
|
||||
<template #title>
|
||||
@@ -127,9 +127,9 @@ const roleDescription = computed(() => {
|
||||
<div>
|
||||
<InputLabel for="role" value="Role" />
|
||||
<MemberRoleSelect
|
||||
v-model="memberBody.role"
|
||||
class="mt-2"
|
||||
name="role"
|
||||
v-model="memberBody.role"></MemberRoleSelect>
|
||||
name="role"></MemberRoleSelect>
|
||||
</div>
|
||||
<div class="flex-1 text-xs flex items-center pt-6">
|
||||
<p>{{ roleDescription }}</p>
|
||||
@@ -140,28 +140,28 @@ const roleDescription = computed(() => {
|
||||
<div>
|
||||
<InputLabel for="billableType" value="Billable" />
|
||||
<MemberBillableSelect
|
||||
class="mt-2"
|
||||
name="billableType"
|
||||
v-model="
|
||||
billableRateSelect
|
||||
"></MemberBillableSelect>
|
||||
"
|
||||
class="mt-2"
|
||||
name="billableType"></MemberBillableSelect>
|
||||
</div>
|
||||
<div
|
||||
class="flex-1"
|
||||
v-if="billableRateSelect === 'custom-rate'">
|
||||
v-if="billableRateSelect === 'custom-rate'"
|
||||
class="flex-1">
|
||||
<InputLabel
|
||||
for="memberBillableRate"
|
||||
class="mb-2"
|
||||
value="Billable Rate" />
|
||||
<BillableRateInput
|
||||
v-model="
|
||||
memberBody.billable_rate
|
||||
"
|
||||
focus
|
||||
class="w-full"
|
||||
:currency="getOrganizationCurrencyString()"
|
||||
@keydown.enter="saveWithChecks()"
|
||||
name="memberBillableRate"
|
||||
v-model="
|
||||
memberBody.billable_rate
|
||||
"></BillableRateInput>
|
||||
@keydown.enter="saveWithChecks()"></BillableRateInput>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -112,11 +112,11 @@ useFocus(clientNameInput, { initialValue: true });
|
||||
v-if="isBillingActivated() && canManageBilling()"
|
||||
href="/billing">
|
||||
<PrimaryButton
|
||||
type="button"
|
||||
class="mt-6"
|
||||
v-if="
|
||||
isBillingActivated() && canUpdateOrganization()
|
||||
">
|
||||
"
|
||||
type="button"
|
||||
class="mt-6">
|
||||
<CreditCardIcon class="w-5 h-5 me-2" />
|
||||
Go to Billing
|
||||
</PrimaryButton>
|
||||
@@ -128,15 +128,15 @@ useFocus(clientNameInput, { initialValue: true });
|
||||
<InputLabel for="email" value="Email" />
|
||||
<TextInput
|
||||
id="email"
|
||||
name="email"
|
||||
ref="memberEmailInput"
|
||||
v-model="addTeamMemberForm.email"
|
||||
name="email"
|
||||
type="text"
|
||||
placeholder="Member Email"
|
||||
@keydown.enter="submit"
|
||||
class="mt-1 block w-full"
|
||||
required
|
||||
autocomplete="memberName" />
|
||||
autocomplete="memberName"
|
||||
@keydown.enter="submit" />
|
||||
<InputError :message="errors.email" class="mt-2" />
|
||||
</div>
|
||||
|
||||
|
||||
@@ -20,19 +20,19 @@ const props = defineProps<{
|
||||
<div class="min-w-[150px]">
|
||||
<button
|
||||
v-if="canUpdateMembers()"
|
||||
@click="emit('edit')"
|
||||
: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-white 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 w-full px-3 py-2.5 text-start text-sm font-medium leading-5 text-white hover:bg-card-background-active focus:outline-none focus:bg-card-background-active transition duration-150 ease-in-out"
|
||||
@click="emit('edit')">
|
||||
<PencilSquareIcon
|
||||
class="w-5 text-icon-active"></PencilSquareIcon>
|
||||
<span>Edit</span>
|
||||
</button>
|
||||
<button
|
||||
v-if="canDeleteMembers()"
|
||||
@click="emit('delete')"
|
||||
: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-white 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 w-full px-3 py-2.5 text-start text-sm font-medium leading-5 text-white hover:bg-card-background-active focus:outline-none focus:bg-card-background-active transition duration-150 ease-in-out"
|
||||
@click="emit('delete')">
|
||||
<TrashIcon class="w-5 text-icon-active"></TrashIcon>
|
||||
<span>Delete</span>
|
||||
</button>
|
||||
|
||||
@@ -18,7 +18,7 @@ function getNameForItem(item: Member) {
|
||||
|
||||
<template>
|
||||
<MultiselectDropdown
|
||||
searchPlaceholder="Search for a Member..."
|
||||
search-placeholder="Search for a Member..."
|
||||
:items="members"
|
||||
:get-key-from-item="getKeyFromItem"
|
||||
:get-name-for-item="getNameForItem">
|
||||
|
||||
@@ -36,9 +36,9 @@ const emit = defineEmits<{
|
||||
<SecondaryButton @click="show = false"> Cancel</SecondaryButton>
|
||||
<PrimaryButton
|
||||
class="ms-3"
|
||||
@click="emit('submit')"
|
||||
:class="{ 'opacity-25': saving }"
|
||||
:disabled="saving">
|
||||
:disabled="saving"
|
||||
@click="emit('submit')">
|
||||
Confirm Transfer
|
||||
</PrimaryButton>
|
||||
</template>
|
||||
|
||||
@@ -89,8 +89,8 @@ async function invitePlaceholder(id: string) {
|
||||
member.is_placeholder === true &&
|
||||
canInvitePlaceholderMembers()
|
||||
"
|
||||
@click="invitePlaceholder(member.id)"
|
||||
size="small"
|
||||
@click="invitePlaceholder(member.id)"
|
||||
>Invite</SecondaryButton
|
||||
>
|
||||
<MemberMoreOptionsDropdown
|
||||
@@ -99,8 +99,8 @@ async function invitePlaceholder(id: string) {
|
||||
@delete="removeMember"></MemberMoreOptionsDropdown>
|
||||
</div>
|
||||
<MemberEditModal
|
||||
:member="member"
|
||||
v-model:show="showEditMemberModal"></MemberEditModal>
|
||||
v-model:show="showEditMemberModal"
|
||||
:member="member"></MemberEditModal>
|
||||
</TableRow>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -34,8 +34,8 @@
|
||||
<div class="ml-4 flex flex-shrink-0">
|
||||
<button
|
||||
type="button"
|
||||
@click="show = false"
|
||||
class="inline-flex rounded-md bg-card-background text-muted hover:text-white focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2">
|
||||
class="inline-flex rounded-md bg-card-background text-muted hover:text-white focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
|
||||
@click="show = false">
|
||||
<span class="sr-only">Close</span>
|
||||
<XMarkIcon class="h-5 w-5" aria-hidden="true" />
|
||||
</button>
|
||||
|
||||
@@ -17,10 +17,10 @@ defineEmits<{
|
||||
|
||||
<template>
|
||||
<BillableRateModal
|
||||
@submit="$emit('submit')"
|
||||
v-model:show="show"
|
||||
v-model:saving="saving"
|
||||
title="Update Organization Billable Rate">
|
||||
title="Update Organization Billable Rate"
|
||||
@submit="$emit('submit')">
|
||||
<p class="py-0.5 text-center">
|
||||
The organization billable rate will be updated to
|
||||
<strong>{{
|
||||
|
||||
@@ -40,7 +40,7 @@ const shownProjects = computed(() => {
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
border: boolean;
|
||||
border?: boolean;
|
||||
}>(),
|
||||
{
|
||||
border: true,
|
||||
@@ -123,17 +123,17 @@ function updateValue(project: Project) {
|
||||
<template #content>
|
||||
<ComboboxRoot
|
||||
:open="open"
|
||||
:modelValue="currentProject"
|
||||
@update:modelValue="updateValue"
|
||||
@update:searchTerm="(e) => console.log(e)"
|
||||
:searchTerm="searchValue"
|
||||
class="relative">
|
||||
:model-value="currentProject"
|
||||
:search-term="searchValue"
|
||||
class="relative"
|
||||
@update:model-value="updateValue"
|
||||
@update:search-term="(e) => console.log(e)">
|
||||
<ComboboxAnchor>
|
||||
<ComboboxInput
|
||||
@keydown.enter="addProjectIfNoneExists"
|
||||
ref="searchInput"
|
||||
class="bg-card-background border-0 placeholder-muted text-sm text-white py-2.5 focus:ring-0 border-b border-card-background-separator focus:border-card-background-separator w-full"
|
||||
placeholder="Search for a project..." />
|
||||
placeholder="Search for a project..."
|
||||
@keydown.enter="addProjectIfNoneExists" />
|
||||
</ComboboxAnchor>
|
||||
<ComboboxContent>
|
||||
<ComboboxViewport
|
||||
|
||||
@@ -90,8 +90,8 @@ async function submitBillableRate() {
|
||||
<div class="text-center">
|
||||
<InputLabel for="color" value="Color" />
|
||||
<ProjectColorSelector
|
||||
class="mt-1"
|
||||
v-model="project.color"></ProjectColorSelector>
|
||||
v-model="project.color"
|
||||
class="mt-1"></ProjectColorSelector>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full">
|
||||
@@ -102,18 +102,18 @@ async function submitBillableRate() {
|
||||
v-model="project.name"
|
||||
type="text"
|
||||
placeholder="Project Name"
|
||||
@keydown.enter="submit()"
|
||||
class="mt-1 block w-full"
|
||||
required
|
||||
autocomplete="projectName" />
|
||||
autocomplete="projectName"
|
||||
@keydown.enter="submit()" />
|
||||
</div>
|
||||
<div class="">
|
||||
<InputLabel for="client" value="Client" />
|
||||
<ClientDropdown
|
||||
:createClient
|
||||
v-model="project.client_id"
|
||||
:create-client
|
||||
:clients="clients"
|
||||
class="mt-1"
|
||||
v-model="project.client_id">
|
||||
class="mt-1">
|
||||
<template #trigger>
|
||||
<Badge
|
||||
class="bg-input-background cursor-pointer hover:bg-tertiary"
|
||||
@@ -133,18 +133,18 @@ async function submitBillableRate() {
|
||||
<div class="lg:grid grid-cols-2 gap-12">
|
||||
<div>
|
||||
<ProjectEditBillableSection
|
||||
@submit="submit"
|
||||
:currency="getOrganizationCurrencyString()"
|
||||
v-model:isBillable="project.is_billable"
|
||||
v-model:billableRate="
|
||||
v-model:is-billable="project.is_billable"
|
||||
v-model:billable-rate="
|
||||
project.billable_rate
|
||||
"></ProjectEditBillableSection>
|
||||
"
|
||||
:currency="getOrganizationCurrencyString()"
|
||||
@submit="submit"></ProjectEditBillableSection>
|
||||
</div>
|
||||
<div>
|
||||
<EstimatedTimeSection
|
||||
v-if="isAllowedToPerformPremiumAction()"
|
||||
@submit="submit()"
|
||||
v-model="project.estimated_time"></EstimatedTimeSection>
|
||||
v-model="project.estimated_time"
|
||||
@submit="submit()"></EstimatedTimeSection>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -163,9 +163,9 @@ async function submitBillableRate() {
|
||||
<ProjectBillableRateModal
|
||||
v-model:show="showBillableRateModal"
|
||||
:currency="getOrganizationCurrencyString()"
|
||||
@submit="submitBillableRate"
|
||||
:new-billable-rate="project.billable_rate"
|
||||
:project-name="project.name"></ProjectBillableRateModal>
|
||||
:project-name="project.name"
|
||||
@submit="submitBillableRate"></ProjectBillableRateModal>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
|
||||
@@ -21,29 +21,29 @@ const props = defineProps<{
|
||||
<MoreOptionsDropdown :label="'Actions for Project ' + props.project.name">
|
||||
<div class="min-w-[150px]">
|
||||
<button
|
||||
@click.prevent="emit('edit')"
|
||||
v-if="canUpdateProjects()"
|
||||
:aria-label="'Edit Project ' + props.project.name"
|
||||
data-testid="project_edit"
|
||||
class="flex items-center space-x-3 w-full px-3 py-2.5 text-start text-sm font-medium leading-5 text-white 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 w-full px-3 py-2.5 text-start text-sm font-medium leading-5 text-white hover:bg-card-background-active focus:outline-none focus:bg-card-background-active transition duration-150 ease-in-out"
|
||||
@click.prevent="emit('edit')">
|
||||
<PencilSquareIcon
|
||||
class="w-5 text-icon-active"></PencilSquareIcon>
|
||||
<span>Edit</span>
|
||||
</button>
|
||||
<button
|
||||
@click.prevent="emit('archive')"
|
||||
v-if="canUpdateProjects()"
|
||||
:aria-label="'Archive Project ' + props.project.name"
|
||||
class="flex items-center space-x-3 w-full px-3 py-2.5 text-start text-sm font-medium leading-5 text-white 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 w-full px-3 py-2.5 text-start text-sm font-medium leading-5 text-white hover:bg-card-background-active focus:outline-none focus:bg-card-background-active transition duration-150 ease-in-out"
|
||||
@click.prevent="emit('archive')">
|
||||
<ArchiveBoxIcon class="w-5 text-icon-active"></ArchiveBoxIcon>
|
||||
<span>{{ project.is_archived ? 'Unarchive' : 'Archive' }}</span>
|
||||
</button>
|
||||
<button
|
||||
@click.prevent="emit('delete')"
|
||||
v-if="canDeleteProjects()"
|
||||
:aria-label="'Delete Project ' + props.project.name"
|
||||
data-testid="project_delete"
|
||||
v-if="canDeleteProjects()"
|
||||
class="border-b border-card-background-separator flex items-center space-x-3 w-full px-3 py-2.5 text-start text-sm font-medium leading-5 text-white hover:bg-card-background-active focus:outline-none focus:bg-card-background-active transition duration-150 ease-in-out">
|
||||
class="border-b border-card-background-separator flex items-center space-x-3 w-full px-3 py-2.5 text-start text-sm font-medium leading-5 text-white hover:bg-card-background-active focus:outline-none focus:bg-card-background-active transition duration-150 ease-in-out"
|
||||
@click.prevent="emit('delete')">
|
||||
<TrashIcon class="w-5 text-icon-active"></TrashIcon>
|
||||
<span>Delete</span>
|
||||
</button>
|
||||
|
||||
@@ -18,7 +18,7 @@ function getNameForItem(item: Project) {
|
||||
|
||||
<template>
|
||||
<MultiselectDropdown
|
||||
searchPlaceholder="Search for a Project..."
|
||||
search-placeholder="Search for a Project..."
|
||||
:items="projects"
|
||||
:get-key-from-item="getKeyFromItem"
|
||||
:get-name-for-item="getNameForItem">
|
||||
|
||||
@@ -44,12 +44,12 @@ import { isAllowedToPerformPremiumAction } from '@/utils/billing';
|
||||
|
||||
<template>
|
||||
<ProjectCreateModal
|
||||
:createProject
|
||||
:createClient
|
||||
v-model:show="showCreateProjectModal"
|
||||
:create-project
|
||||
:create-client
|
||||
:currency="getOrganizationCurrencyString()"
|
||||
:clients="clients"
|
||||
:enableEstimatedTime="isAllowedToPerformPremiumAction"
|
||||
v-model:show="showCreateProjectModal"></ProjectCreateModal>
|
||||
:enable-estimated-time="isAllowedToPerformPremiumAction"></ProjectCreateModal>
|
||||
<div class="flow-root max-w-[100vw] overflow-x-auto">
|
||||
<div class="inline-block min-w-full align-middle">
|
||||
<div
|
||||
@@ -57,12 +57,12 @@ import { isAllowedToPerformPremiumAction } from '@/utils/billing';
|
||||
class="grid min-w-full"
|
||||
:style="gridTemplate">
|
||||
<ProjectTableHeading
|
||||
:showBillableRate="
|
||||
:show-billable-rate="
|
||||
props.showBillableRate
|
||||
"></ProjectTableHeading>
|
||||
<div
|
||||
class="col-span-5 py-24 text-center"
|
||||
v-if="projects.length === 0">
|
||||
v-if="projects.length === 0"
|
||||
class="col-span-5 py-24 text-center">
|
||||
<FolderPlusIcon
|
||||
class="w-8 text-icon-default inline pb-2"></FolderPlusIcon>
|
||||
<h3 class="text-white font-semibold">
|
||||
@@ -81,14 +81,14 @@ import { isAllowedToPerformPremiumAction } from '@/utils/billing';
|
||||
</p>
|
||||
<SecondaryButton
|
||||
v-if="canCreateProjects()"
|
||||
@click="showCreateProjectModal = true"
|
||||
:icon="PlusIcon"
|
||||
@click="showCreateProjectModal = true"
|
||||
>Create your First Project
|
||||
</SecondaryButton>
|
||||
</div>
|
||||
<template v-for="project in projects" :key="project.id">
|
||||
<ProjectTableRow
|
||||
:showBillableRate="props.showBillableRate"
|
||||
:show-billable-rate="props.showBillableRate"
|
||||
:project="project"></ProjectTableRow>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
@@ -19,8 +19,8 @@ defineProps<{
|
||||
Progress
|
||||
</div>
|
||||
<div
|
||||
class="px-3 py-1.5 text-left font-semibold text-white"
|
||||
v-if="showBillableRate">
|
||||
v-if="showBillableRate"
|
||||
class="px-3 py-1.5 text-left font-semibold text-white">
|
||||
Billable Rate
|
||||
</div>
|
||||
<div class="px-3 py-1.5 text-left font-semibold text-white">Status</div>
|
||||
|
||||
@@ -83,8 +83,8 @@ const showEditProjectModal = ref(false);
|
||||
</div>
|
||||
<div class="whitespace-nowrap min-w-0 px-3 py-4 text-sm text-muted">
|
||||
<div
|
||||
class="overflow-ellipsis overflow-hidden"
|
||||
v-if="project.client_id">
|
||||
v-if="project.client_id"
|
||||
class="overflow-ellipsis overflow-hidden">
|
||||
{{ client?.name }}
|
||||
</div>
|
||||
<div v-else>No client</div>
|
||||
@@ -106,8 +106,8 @@ const showEditProjectModal = ref(false);
|
||||
<span v-else> -- </span>
|
||||
</div>
|
||||
<div
|
||||
class="whitespace-nowrap px-3 py-4 text-sm text-muted"
|
||||
v-if="showBillableRate">
|
||||
v-if="showBillableRate"
|
||||
class="whitespace-nowrap px-3 py-4 text-sm text-muted">
|
||||
{{ billableRateInfo }}
|
||||
</div>
|
||||
<div
|
||||
|
||||
@@ -18,10 +18,10 @@ defineEmits<{
|
||||
|
||||
<template>
|
||||
<BillableRateModal
|
||||
@submit="$emit('submit')"
|
||||
v-model:show="show"
|
||||
v-model:saving="saving"
|
||||
title="Update Project Member Billable Rate">
|
||||
title="Update Project Member Billable Rate"
|
||||
@submit="$emit('submit')">
|
||||
<p class="py-1 text-center">
|
||||
The billable rate of {{ memberName }} will be updated to
|
||||
<strong>{{
|
||||
|
||||
@@ -52,16 +52,16 @@ useFocus(projectNameInput, { initialValue: true });
|
||||
<div class="grid grid-cols-3 items-center space-x-4">
|
||||
<div class="col-span-3 sm:col-span-2">
|
||||
<MemberCombobox
|
||||
:hidden-members="props.existingMembers"
|
||||
v-model="projectMember.member_id"></MemberCombobox>
|
||||
v-model="projectMember.member_id"
|
||||
:hidden-members="props.existingMembers"></MemberCombobox>
|
||||
</div>
|
||||
<div class="col-span-3 sm:col-span-1 flex-1">
|
||||
<BillableRateInput
|
||||
name="billable_rate"
|
||||
:currency="getOrganizationCurrencyString()"
|
||||
v-model="
|
||||
projectMember.billable_rate
|
||||
"></BillableRateInput>
|
||||
"
|
||||
name="billable_rate"
|
||||
:currency="getOrganizationCurrencyString()"></BillableRateInput>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -75,8 +75,8 @@ useFocus(projectNameInput, { initialValue: true });
|
||||
|
||||
<template #content>
|
||||
<ProjectMemberBillableRateModal
|
||||
:member-name="props.name"
|
||||
v-model:show="showBillableRateModal"
|
||||
:member-name="props.name"
|
||||
:new-billable-rate="projectMemberBody.billable_rate"
|
||||
@close="showBillableRateModal = false"
|
||||
@submit="submitBillableRate"></ProjectMemberBillableRateModal>
|
||||
@@ -92,12 +92,12 @@ useFocus(projectNameInput, { initialValue: true });
|
||||
class="mb-2"
|
||||
value="Billable Rate"></InputLabel>
|
||||
<BillableRateInput
|
||||
@keydown.enter="submit"
|
||||
:currency="getOrganizationCurrencyString()"
|
||||
name="billable_rate"
|
||||
v-model="
|
||||
projectMemberBody.billable_rate
|
||||
"></BillableRateInput>
|
||||
"
|
||||
:currency="getOrganizationCurrencyString()"
|
||||
name="billable_rate"
|
||||
@keydown.enter="submit"></BillableRateInput>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -27,17 +27,17 @@ const currentMember = computed(() => {
|
||||
<MoreOptionsDropdown
|
||||
:label="'Actions for Project Member ' + currentMember?.name">
|
||||
<button
|
||||
@click.prevent="emit('edit')"
|
||||
:aria-label="'Edit Project Member ' + currentMember?.name"
|
||||
class="flex items-center space-x-3 w-full px-3 py-2.5 text-start text-sm font-medium leading-5 text-white 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 w-full px-3 py-2.5 text-start text-sm font-medium leading-5 text-white hover:bg-card-background-active focus:outline-none focus:bg-card-background-active transition duration-150 ease-in-out"
|
||||
@click.prevent="emit('edit')">
|
||||
<PencilSquareIcon class="w-5 text-icon-active"></PencilSquareIcon>
|
||||
<span>Edit</span>
|
||||
</button>
|
||||
<button
|
||||
@click.prevent="emit('delete')"
|
||||
:aria-label="'Delete Project Member ' + currentMember?.name"
|
||||
data-testid="project_delete"
|
||||
class="flex items-center space-x-3 w-full px-3 py-2.5 text-start text-sm font-medium leading-5 text-white 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 w-full px-3 py-2.5 text-start text-sm font-medium leading-5 text-white hover:bg-card-background-active focus:outline-none focus:bg-card-background-active transition duration-150 ease-in-out"
|
||||
@click.prevent="emit('delete')">
|
||||
<TrashIcon class="w-5 text-icon-active"></TrashIcon>
|
||||
<span>Remove from Team</span>
|
||||
</button>
|
||||
|
||||
@@ -18,9 +18,9 @@ const createProjectMember = ref(false);
|
||||
|
||||
<template>
|
||||
<ProjectMemberCreateModal
|
||||
v-model:show="createProjectMember"
|
||||
:existing-members="projectMembers"
|
||||
:project-id="projectId"
|
||||
v-model:show="createProjectMember"></ProjectMemberCreateModal>
|
||||
:project-id="projectId"></ProjectMemberCreateModal>
|
||||
<div class="flow-root">
|
||||
<div class="inline-block min-w-full align-middle">
|
||||
<div
|
||||
@@ -29,15 +29,15 @@ const createProjectMember = ref(false);
|
||||
style="grid-template-columns: 1fr 150px 150px 80px">
|
||||
<ProjectMemberTableHeading></ProjectMemberTableHeading>
|
||||
<div
|
||||
class="col-span-5 py-24 text-center"
|
||||
v-if="projectMembers.length === 0">
|
||||
v-if="projectMembers.length === 0"
|
||||
class="col-span-5 py-24 text-center">
|
||||
<UserGroupIcon
|
||||
class="w-8 text-icon-default inline pb-2"></UserGroupIcon>
|
||||
<h3 class="text-white font-semibold">No project members</h3>
|
||||
<p class="pb-5">Add the first project member!</p>
|
||||
<SecondaryButton
|
||||
@click="createProjectMember = true"
|
||||
:icon="PlusIcon"
|
||||
@click="createProjectMember = true"
|
||||
>Add a new Project Member
|
||||
</SecondaryButton>
|
||||
</div>
|
||||
|
||||
@@ -37,8 +37,8 @@ const showEditModal = ref(false);
|
||||
<template>
|
||||
<TableRow>
|
||||
<ProjectMemberEditModal
|
||||
:name="member?.name"
|
||||
v-model:show="showEditModal"
|
||||
:name="member?.name"
|
||||
:project-member="projectMember"></ProjectMemberEditModal>
|
||||
<div
|
||||
class="whitespace-nowrap flex items-center space-x-5 3xl:pl-12 py-4 pr-3 text-sm font-medium text-white pl-4 sm:pl-6 lg:pl-8 3xl:pl-12">
|
||||
|
||||
@@ -82,22 +82,22 @@ async function submit() {
|
||||
<InputLabel for="name" value="Name" />
|
||||
<TextInput
|
||||
id="name"
|
||||
class="mt-1.5 w-full"
|
||||
v-model="report.name"></TextInput>
|
||||
v-model="report.name"
|
||||
class="mt-1.5 w-full"></TextInput>
|
||||
</div>
|
||||
<div>
|
||||
<InputLabel for="description" value="Description" />
|
||||
<TextInput
|
||||
id="description"
|
||||
class="mt-1.5 w-full"
|
||||
v-model="report.description"></TextInput>
|
||||
v-model="report.description"
|
||||
class="mt-1.5 w-full"></TextInput>
|
||||
</div>
|
||||
<InputLabel value="Visibility" />
|
||||
<div class="flex items-center space-x-12">
|
||||
<div class="flex items-center space-x-3 px-2 py-3">
|
||||
<Checkbox
|
||||
v-model:checked="report.is_public"
|
||||
id="is_public"></Checkbox>
|
||||
id="is_public"
|
||||
v-model:checked="report.is_public"></Checkbox>
|
||||
<InputLabel for="is_public" value="Public" />
|
||||
</div>
|
||||
<div
|
||||
|
||||
@@ -96,22 +96,22 @@ async function submit() {
|
||||
<InputLabel for="name" value="Name" />
|
||||
<TextInput
|
||||
id="name"
|
||||
class="mt-1.5 w-full"
|
||||
v-model="report.name"></TextInput>
|
||||
v-model="report.name"
|
||||
class="mt-1.5 w-full"></TextInput>
|
||||
</div>
|
||||
<div>
|
||||
<InputLabel for="description" value="Description" />
|
||||
<TextInput
|
||||
id="description"
|
||||
class="mt-1.5 w-full"
|
||||
v-model="report.description"></TextInput>
|
||||
v-model="report.description"
|
||||
class="mt-1.5 w-full"></TextInput>
|
||||
</div>
|
||||
<InputLabel value="Visibility" />
|
||||
<div class="flex items-center space-x-12">
|
||||
<div class="flex items-center space-x-2 px-2 py-3">
|
||||
<Checkbox
|
||||
v-model:checked="report.is_public"
|
||||
id="is_public"></Checkbox>
|
||||
id="is_public"
|
||||
v-model:checked="report.is_public"></Checkbox>
|
||||
<InputLabel for="is_public" value="Public" />
|
||||
</div>
|
||||
<div
|
||||
|
||||
@@ -17,19 +17,19 @@ const props = defineProps<{
|
||||
<MoreOptionsDropdown :label="'Actions for Project ' + props.report.name">
|
||||
<div class="min-w-[150px]">
|
||||
<button
|
||||
@click.prevent="emit('edit')"
|
||||
v-if="canUpdateReport()"
|
||||
:aria-label="'Edit Report ' + props.report.name"
|
||||
class="flex items-center space-x-3 w-full px-3 py-2.5 text-start text-sm font-medium leading-5 text-white 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 w-full px-3 py-2.5 text-start text-sm font-medium leading-5 text-white hover:bg-card-background-active focus:outline-none focus:bg-card-background-active transition duration-150 ease-in-out"
|
||||
@click.prevent="emit('edit')">
|
||||
<PencilSquareIcon
|
||||
class="w-5 text-icon-active"></PencilSquareIcon>
|
||||
<span>Edit</span>
|
||||
</button>
|
||||
<button
|
||||
@click.prevent="emit('delete')"
|
||||
:aria-label="'Delete Report ' + props.report.name"
|
||||
v-if="canDeleteReport()"
|
||||
class="border-b border-card-background-separator flex items-center space-x-3 w-full px-3 py-2.5 text-start text-sm font-medium leading-5 text-white hover:bg-card-background-active focus:outline-none focus:bg-card-background-active transition duration-150 ease-in-out">
|
||||
:aria-label="'Delete Report ' + props.report.name"
|
||||
class="border-b border-card-background-separator flex items-center space-x-3 w-full px-3 py-2.5 text-start text-sm font-medium leading-5 text-white hover:bg-card-background-active focus:outline-none focus:bg-card-background-active transition duration-150 ease-in-out"
|
||||
@click.prevent="emit('delete')">
|
||||
<TrashIcon class="w-5 text-icon-active"></TrashIcon>
|
||||
<span>Delete</span>
|
||||
</button>
|
||||
|
||||
@@ -27,8 +27,8 @@ function onSaveReportClick() {
|
||||
|
||||
<template>
|
||||
<ReportCreateModal
|
||||
:properties="reportProperties"
|
||||
v-model:show="showCreateReportModal"></ReportCreateModal>
|
||||
v-model:show="showCreateReportModal"
|
||||
:properties="reportProperties"></ReportCreateModal>
|
||||
<UpgradeModal v-model:show="showPremiumModal">
|
||||
<strong>Sharable Reports</strong> is only available in solidtime
|
||||
Professional.
|
||||
|
||||
@@ -27,19 +27,19 @@ const gridTemplate = computed(() => {
|
||||
:style="gridTemplate">
|
||||
<ReportTableHeading></ReportTableHeading>
|
||||
<div
|
||||
class="col-span-5 py-24 text-center"
|
||||
v-if="reports.length === 0">
|
||||
v-if="reports.length === 0"
|
||||
class="col-span-5 py-24 text-center">
|
||||
<FolderPlusIcon
|
||||
class="w-8 text-icon-default inline pb-2"></FolderPlusIcon>
|
||||
<h3 class="text-white font-semibold">
|
||||
No shared reports found
|
||||
</h3>
|
||||
<p class="pb-5" v-if="canCreateProjects()">
|
||||
<p v-if="canCreateProjects()" class="pb-5">
|
||||
Create your first project now!
|
||||
</p>
|
||||
<SecondaryButton
|
||||
@click="router.visit(route('reporting'))"
|
||||
:icon="PlusIcon"
|
||||
@click="router.visit(route('reporting'))"
|
||||
>Go to the overview to create a report
|
||||
</SecondaryButton>
|
||||
</div>
|
||||
|
||||
@@ -157,7 +157,7 @@ const option = ref({
|
||||
:autoresize="true"
|
||||
class="chart"
|
||||
:option="option" />
|
||||
<div class="chart flex flex-col items-center justify-center" v-else>
|
||||
<div v-else class="chart flex flex-col items-center justify-center">
|
||||
<p class="text-lg text-white font-semibold">
|
||||
No time entries found
|
||||
</p>
|
||||
|
||||
@@ -22,8 +22,8 @@ function downloadCurrentExport() {
|
||||
<Modal
|
||||
closeable
|
||||
max-width="lg"
|
||||
@close="showExportModal = false"
|
||||
:show="showExportModal">
|
||||
:show="showExportModal"
|
||||
@close="showExportModal = false">
|
||||
<button
|
||||
class="text-text-tertiary w-6 mx-auto absolute focus-visible:outline-none focus-visible:ring-2 rounded-full focus-visible:ring-ring transition focus-visible:text-text-primary hover:text-text-primary top-2 right-2">
|
||||
<XMarkIcon @click="showExportModal = false"></XMarkIcon>
|
||||
|
||||
@@ -23,7 +23,7 @@ const title = computed(() => {
|
||||
:get-key-from-item="(item) => item.value"
|
||||
:get-name-for-item="(item) => item.label"
|
||||
:items="groupByOptions">
|
||||
<template v-slot:trigger>
|
||||
<template #trigger>
|
||||
<Badge
|
||||
size="large"
|
||||
class="cursor-pointer hover:bg-card-background transition space-x-5 flex">
|
||||
|
||||
@@ -35,9 +35,9 @@ const expanded = ref(false);
|
||||
)
|
||||
">
|
||||
<GroupedItemsCountButton
|
||||
v-if="entry.grouped_data && entry.grouped_data?.length > 0"
|
||||
:expanded="expanded"
|
||||
@click="expanded = !expanded"
|
||||
v-if="entry.grouped_data && entry.grouped_data?.length > 0">
|
||||
@click="expanded = !expanded">
|
||||
{{ entry.grouped_data?.length }}
|
||||
</GroupedItemsCountButton>
|
||||
<span>
|
||||
@@ -52,13 +52,13 @@ const expanded = ref(false);
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="expanded && entry.grouped_data"
|
||||
class="col-span-3 grid bg-quaternary"
|
||||
style="grid-template-columns: 1fr 150px 150px"
|
||||
v-if="expanded && entry.grouped_data">
|
||||
style="grid-template-columns: 1fr 150px 150px">
|
||||
<ReportingRow
|
||||
indent
|
||||
v-for="subEntry in entry.grouped_data"
|
||||
:key="subEntry.description ?? 'none'"
|
||||
indent
|
||||
:entry="subEntry"></ReportingRow>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -10,18 +10,18 @@ defineProps<{
|
||||
<template>
|
||||
<TabBar>
|
||||
<TabBarItem
|
||||
@click="router.visit(route('reporting'))"
|
||||
:active="active === 'reporting'"
|
||||
@click="router.visit(route('reporting'))"
|
||||
>Overview</TabBarItem
|
||||
>
|
||||
<TabBarItem
|
||||
@click="router.visit(route('reporting.detailed'))"
|
||||
:active="active === 'detailed'"
|
||||
@click="router.visit(route('reporting.detailed'))"
|
||||
>Detailed</TabBarItem
|
||||
>
|
||||
<TabBarItem
|
||||
@click="router.visit(route('reporting.shared'))"
|
||||
:active="active === 'shared'"
|
||||
@click="router.visit(route('reporting.shared'))"
|
||||
>Shared</TabBarItem
|
||||
>
|
||||
</TabBar>
|
||||
|
||||
@@ -14,10 +14,10 @@ const props = defineProps<{
|
||||
<template>
|
||||
<MoreOptionsDropdown :label="'Actions for Tag ' + props.tag.name">
|
||||
<button
|
||||
@click="emit('delete')"
|
||||
:aria-label="'Delete Tag ' + props.tag.name"
|
||||
data-testid="tag_delete"
|
||||
class="flex items-center space-x-3 w-full px-3 py-2.5 text-start text-sm font-medium leading-5 text-white 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 w-full px-3 py-2.5 text-start text-sm font-medium leading-5 text-white hover:bg-card-background-active focus:outline-none focus:bg-card-background-active transition duration-150 ease-in-out"
|
||||
@click="emit('delete')">
|
||||
<TrashIcon class="w-5 text-icon-active"></TrashIcon>
|
||||
<span>Delete</span>
|
||||
</button>
|
||||
|
||||
@@ -19,8 +19,8 @@ const showCreateTagModal = ref(false);
|
||||
|
||||
<template>
|
||||
<TagCreateModal
|
||||
:createTag
|
||||
v-model:show="showCreateTagModal"></TagCreateModal>
|
||||
v-model:show="showCreateTagModal"
|
||||
:create-tag></TagCreateModal>
|
||||
<div class="flow-root">
|
||||
<div class="inline-block min-w-full align-middle">
|
||||
<div
|
||||
@@ -29,18 +29,18 @@ const showCreateTagModal = ref(false);
|
||||
style="grid-template-columns: 1fr 80px">
|
||||
<TagTableHeading></TagTableHeading>
|
||||
<div
|
||||
class="col-span-5 py-24 text-center"
|
||||
v-if="tags.length === 0">
|
||||
v-if="tags.length === 0"
|
||||
class="col-span-5 py-24 text-center">
|
||||
<FolderPlusIcon
|
||||
class="w-8 text-icon-default inline pb-2"></FolderPlusIcon>
|
||||
<h3 class="text-white font-semibold">No tags found</h3>
|
||||
<p class="pb-5" v-if="canCreateTags()">
|
||||
<p v-if="canCreateTags()" class="pb-5">
|
||||
Create your first tag now!
|
||||
</p>
|
||||
<SecondaryButton
|
||||
v-if="canCreateTags()"
|
||||
@click="showCreateTagModal = true"
|
||||
:icon="PlusIcon"
|
||||
@click="showCreateTagModal = true"
|
||||
>Create your First Tag</SecondaryButton
|
||||
>
|
||||
</div>
|
||||
|
||||
@@ -53,19 +53,19 @@ useFocus(taskNameInput, { initialValue: true });
|
||||
v-model="taskName"
|
||||
type="text"
|
||||
placeholder="Task Name"
|
||||
@keydown.enter="submit()"
|
||||
class="mt-1 block w-full"
|
||||
required
|
||||
autocomplete="taskName" />
|
||||
autocomplete="taskName"
|
||||
@keydown.enter="submit()" />
|
||||
</div>
|
||||
<div class="col-span-6 sm:col-span-4">
|
||||
<ProjectDropdown :modelValue="projectId"></ProjectDropdown>
|
||||
<ProjectDropdown :model-value="projectId"></ProjectDropdown>
|
||||
</div>
|
||||
</div>
|
||||
<EstimatedTimeSection
|
||||
v-if="isAllowedToPerformPremiumAction()"
|
||||
@submit="submit()"
|
||||
v-model="estimatedTime"></EstimatedTimeSection>
|
||||
v-model="estimatedTime"
|
||||
@submit="submit()"></EstimatedTimeSection>
|
||||
</template>
|
||||
<template #footer>
|
||||
<SecondaryButton @click="show = false"> Cancel </SecondaryButton>
|
||||
|
||||
@@ -50,16 +50,16 @@ useFocus(taskNameInput, { initialValue: true });
|
||||
v-model="taskBody.name"
|
||||
type="text"
|
||||
placeholder="Task Name"
|
||||
@keydown.enter="submit()"
|
||||
class="mt-1 block w-full"
|
||||
required
|
||||
autocomplete="taskName" />
|
||||
autocomplete="taskName"
|
||||
@keydown.enter="submit()" />
|
||||
</div>
|
||||
</div>
|
||||
<EstimatedTimeSection
|
||||
v-if="isAllowedToPerformPremiumAction()"
|
||||
@submit="submit()"
|
||||
v-model="taskBody.estimated_time"></EstimatedTimeSection>
|
||||
v-model="taskBody.estimated_time"
|
||||
@submit="submit()"></EstimatedTimeSection>
|
||||
</template>
|
||||
<template #footer>
|
||||
<SecondaryButton @click="show = false"> Cancel </SecondaryButton>
|
||||
|
||||
@@ -21,30 +21,30 @@ const props = defineProps<{
|
||||
<MoreOptionsDropdown :label="'Actions for Task ' + props.task.name">
|
||||
<div class="min-w-[150px]">
|
||||
<button
|
||||
@click="emit('edit')"
|
||||
v-if="canUpdateTasks()"
|
||||
:aria-label="'Edit Task ' + props.task.name"
|
||||
data-testid="task_edit"
|
||||
class="flex items-center space-x-3 w-full px-3 py-2.5 text-start text-sm font-medium leading-5 text-white 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 w-full px-3 py-2.5 text-start text-sm font-medium leading-5 text-white hover:bg-card-background-active focus:outline-none focus:bg-card-background-active transition duration-150 ease-in-out"
|
||||
@click="emit('edit')">
|
||||
<PencilSquareIcon
|
||||
class="w-5 text-icon-active"></PencilSquareIcon>
|
||||
<span>Edit</span>
|
||||
</button>
|
||||
<button
|
||||
@click="emit('done')"
|
||||
v-if="canUpdateTasks()"
|
||||
:aria-label="'Mark Task ' + props.task.name + ' as done'"
|
||||
class="flex items-center space-x-3 w-full px-3 py-2.5 text-start text-sm font-medium leading-5 text-white 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 w-full px-3 py-2.5 text-start text-sm font-medium leading-5 text-white hover:bg-card-background-active focus:outline-none focus:bg-card-background-active transition duration-150 ease-in-out"
|
||||
@click="emit('done')">
|
||||
<CheckCircleIcon class="w-5 text-icon-active"></CheckCircleIcon>
|
||||
<span v-if="props.task.is_done">Mark as active</span>
|
||||
<span v-else>Mark as done</span>
|
||||
</button>
|
||||
<button
|
||||
@click="emit('delete')"
|
||||
:aria-label="'Delete Task ' + props.task.name"
|
||||
v-if="canDeleteTasks()"
|
||||
:aria-label="'Delete Task ' + props.task.name"
|
||||
data-testid="task_delete"
|
||||
class="flex items-center space-x-3 w-full px-3 py-2.5 text-start text-sm font-medium leading-5 text-white 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 w-full px-3 py-2.5 text-start text-sm font-medium leading-5 text-white hover:bg-card-background-active focus:outline-none focus:bg-card-background-active transition duration-150 ease-in-out"
|
||||
@click="emit('delete')">
|
||||
<TrashIcon class="w-5 text-icon-active"></TrashIcon>
|
||||
<span>Delete</span>
|
||||
</button>
|
||||
|
||||
@@ -18,7 +18,7 @@ function getNameForItem(item: Task) {
|
||||
|
||||
<template>
|
||||
<MultiselectDropdown
|
||||
searchPlaceholder="Search for a Task..."
|
||||
search-placeholder="Search for a Task..."
|
||||
:items="tasks"
|
||||
:get-key-from-item="getKeyFromItem"
|
||||
:get-name-for-item="getNameForItem">
|
||||
|
||||
@@ -19,8 +19,8 @@ const createTask = ref(false);
|
||||
|
||||
<template>
|
||||
<TaskCreateModal
|
||||
:project-id="props.projectId"
|
||||
v-model:show="createTask"></TaskCreateModal>
|
||||
v-model:show="createTask"
|
||||
:project-id="props.projectId"></TaskCreateModal>
|
||||
<div class="flow-root">
|
||||
<div class="inline-block min-w-full align-middle">
|
||||
<div
|
||||
@@ -37,18 +37,18 @@ const createTask = ref(false);
|
||||
">
|
||||
<TaskTableHeading></TaskTableHeading>
|
||||
<div
|
||||
class="col-span-5 py-24 text-center"
|
||||
v-if="tasks.length === 0">
|
||||
v-if="tasks.length === 0"
|
||||
class="col-span-5 py-24 text-center">
|
||||
<PlusCircleIcon
|
||||
class="w-8 text-icon-default inline pb-2"></PlusCircleIcon>
|
||||
<h3 class="text-white font-semibold">No tasks found</h3>
|
||||
<p class="pb-5" v-if="canCreateTasks()">
|
||||
<p v-if="canCreateTasks()" class="pb-5">
|
||||
Create your first task now!
|
||||
</p>
|
||||
<SecondaryButton
|
||||
v-if="canCreateTasks()"
|
||||
@click="createTask = true"
|
||||
:icon="PlusIcon"
|
||||
@click="createTask = true"
|
||||
>Create your First Task
|
||||
</SecondaryButton>
|
||||
</div>
|
||||
|
||||
@@ -75,8 +75,8 @@ const showTaskEditModal = ref(false);
|
||||
@delete="deleteTask"></TaskMoreOptionsDropdown>
|
||||
</div>
|
||||
<TaskEditModal
|
||||
:task="task"
|
||||
v-model:show="showTaskEditModal"></TaskEditModal>
|
||||
v-model:show="showTaskEditModal"
|
||||
:task="task"></TaskEditModal>
|
||||
</TableRow>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@ const showUpgradeModal = ref(false);
|
||||
solidtime Professional.
|
||||
</UpgradeModal>
|
||||
<button
|
||||
@click.prevent="showUpgradeModal = true"
|
||||
class="inline-flex bg-secondary hover:bg-tertiary px-2 py-1 rounded border border-border-secondary hover:border-border-tertiary items-center space-x-1">
|
||||
class="inline-flex bg-secondary hover:bg-tertiary px-2 py-1 rounded border border-border-secondary hover:border-border-tertiary items-center space-x-1"
|
||||
@click.prevent="showUpgradeModal = true">
|
||||
<LockClosedIcon class="w-3 text-text-tertiary"></LockClosedIcon>
|
||||
<span class="text-xs text-text-secondary font-semibold"> Upgrade </span>
|
||||
</button>
|
||||
|
||||
@@ -50,12 +50,12 @@ const show = defineModel('show', { default: false });
|
||||
v-if="isBillingActivated() && canManageBilling()"
|
||||
href="/billing">
|
||||
<PrimaryButton
|
||||
type="button"
|
||||
class="mt-6"
|
||||
:icon="CreditCardIcon"
|
||||
v-if="
|
||||
isBillingActivated() && canUpdateOrganization()
|
||||
">
|
||||
"
|
||||
type="button"
|
||||
class="mt-6"
|
||||
:icon="CreditCardIcon">
|
||||
Go to Billing
|
||||
</PrimaryButton>
|
||||
</Link>
|
||||
|
||||
@@ -32,8 +32,8 @@ const isRunningInDifferentOrganization = computed(() => {
|
||||
<template>
|
||||
<div class="pt-3 pb-2.5 px-2 flex justify-between items-center relative">
|
||||
<div
|
||||
class="absolute w-full h-full backdrop-blur-sm z-10 flex items-center justify-center"
|
||||
v-if="isRunningInDifferentOrganization">
|
||||
v-if="isRunningInDifferentOrganization"
|
||||
class="absolute w-full h-full backdrop-blur-sm z-10 flex items-center justify-center">
|
||||
<div
|
||||
class="w-full h-[calc(100%+10px)] absolute bg-default-background opacity-75 backdrop-blur-sm"></div>
|
||||
<div class="flex space-x-3 items-center w-full z-20 justify-center">
|
||||
@@ -50,7 +50,7 @@ const isRunningInDifferentOrganization = computed(() => {
|
||||
</div>
|
||||
<TimeTrackerStartStop
|
||||
:active="isActive"
|
||||
@changed="setActiveState"
|
||||
size="base"></TimeTrackerStartStop>
|
||||
size="base"
|
||||
@changed="setActiveState"></TimeTrackerStartStop>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -15,8 +15,8 @@ defineProps<{
|
||||
<DashboardCard title="Last 7 Days" :icon="CalendarIcon">
|
||||
<DayOverviewCardEntry
|
||||
v-for="day in last7Days"
|
||||
:class="last7Days.length === 7 ? 'last:border-0 first:pt-3' : ''"
|
||||
:key="day.date"
|
||||
:class="last7Days.length === 7 ? 'last:border-0 first:pt-3' : ''"
|
||||
:date="day.date"
|
||||
:history="day.history"
|
||||
:duration="day.duration"></DayOverviewCardEntry>
|
||||
|
||||
@@ -20,8 +20,8 @@ const props = defineProps<{
|
||||
<DashboardCard title="Recently Tracked Tasks" :icon="CheckCircleIcon">
|
||||
<RecentlyTrackedTasksCardEntry
|
||||
v-for="lastTask in props.latestTasks"
|
||||
:class="props.latestTasks.length === 4 ? 'last:border-0' : ''"
|
||||
:key="lastTask.id"
|
||||
:class="props.latestTasks.length === 4 ? 'last:border-0' : ''"
|
||||
:project_id="lastTask.project_id"
|
||||
:task_id="lastTask.id"
|
||||
:title="lastTask.name"></RecentlyTrackedTasksCardEntry>
|
||||
|
||||
@@ -29,14 +29,14 @@ const open = useSessionStorage('nav-collapse-state-' + props.title, true);
|
||||
:icon
|
||||
:current
|
||||
:href></NavigationSidebarLink>
|
||||
<CollapsibleRoot v-model:open="open" v-else
|
||||
<CollapsibleRoot v-else v-model:open="open"
|
||||
><CollapsibleTrigger class="w-full group py-0.5">
|
||||
<div
|
||||
class="text-muted group-hover:text-white group-hover:bg-menu-active group flex gap-x-2 rounded-md transition leading-6 py-1 px-2 font-medium text-sm items-center justify-between">
|
||||
<div class="flex items-center gap-x-2">
|
||||
<component
|
||||
v-if="icon"
|
||||
:is="icon"
|
||||
v-if="icon"
|
||||
:class="[
|
||||
current
|
||||
? 'text-icon-active'
|
||||
@@ -59,8 +59,8 @@ const open = useSessionStorage('nav-collapse-state-' + props.title, true);
|
||||
<CollapsibleContent class="CollapsibleContent">
|
||||
<div class="px-3.5">
|
||||
<ul
|
||||
class="flex min-w-0 flex-col border-l border-border-secondary px-3 w-full my-0.5"
|
||||
v-if="subItems">
|
||||
v-if="subItems"
|
||||
class="flex min-w-0 flex-col border-l border-border-secondary px-3 w-full my-0.5">
|
||||
<li
|
||||
v-for="subItem in subItems"
|
||||
:key="subItem.title"
|
||||
|
||||
@@ -19,8 +19,8 @@ defineProps<{
|
||||
'group flex gap-x-2 rounded-md transition leading-6 py-1 px-2 font-medium text-sm items-center',
|
||||
]">
|
||||
<component
|
||||
v-if="icon"
|
||||
:is="icon"
|
||||
v-if="icon"
|
||||
:class="[
|
||||
current
|
||||
? 'text-icon-active'
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
<div class="flex w-full flex-col items-center space-y-4 sm:items-end">
|
||||
<Notification
|
||||
v-for="notification in notifications"
|
||||
:type="notification.type"
|
||||
:key="notification.uuid"
|
||||
:type="notification.type"
|
||||
:title="notification.title"
|
||||
:message="notification.message"></Notification>
|
||||
</div>
|
||||
|
||||
@@ -110,29 +110,29 @@ const { tags } = storeToRefs(useTagsStore());
|
||||
<CardTitle title="Time Tracker" :icon="ClockIcon"></CardTitle>
|
||||
<div class="relative">
|
||||
<TimeTrackerRunningInDifferentOrganizationOverlay
|
||||
@switchOrganization="switchToTimeEntryOrganization"
|
||||
v-if="
|
||||
isRunningInDifferentOrganization
|
||||
"></TimeTrackerRunningInDifferentOrganizationOverlay>
|
||||
"
|
||||
@switch-organization="switchToTimeEntryOrganization"></TimeTrackerRunningInDifferentOrganizationOverlay>
|
||||
|
||||
<TimeTrackerControls
|
||||
:createProject
|
||||
:enableEstimatedTime="isAllowedToPerformPremiumAction()"
|
||||
:canCreateProject="canCreateProjects()"
|
||||
:createClient
|
||||
v-model:current-time-entry="currentTimeEntry"
|
||||
v-model:live-timer="now"
|
||||
:create-project
|
||||
:enable-estimated-time="isAllowedToPerformPremiumAction()"
|
||||
:can-create-project="canCreateProjects()"
|
||||
:create-client
|
||||
:clients
|
||||
:tags
|
||||
:tasks
|
||||
:projects
|
||||
:createTag
|
||||
:isActive
|
||||
:create-tag
|
||||
:is-active
|
||||
:currency="getOrganizationCurrencyString()"
|
||||
v-model:currentTimeEntry="currentTimeEntry"
|
||||
v-model:liveTimer="now"
|
||||
@startLiveTimer="startLiveTimer"
|
||||
@stopLiveTimer="stopLiveTimer"
|
||||
@startTimer="setActiveState(true)"
|
||||
@stopTimer="setActiveState(false)"
|
||||
@updateTimeEntry="updateTimeEntry"></TimeTrackerControls>
|
||||
@start-live-timer="startLiveTimer"
|
||||
@stop-live-timer="stopLiveTimer"
|
||||
@start-timer="setActiveState(true)"
|
||||
@stop-timer="setActiveState(false)"
|
||||
@update-time-entry="updateTimeEntry"></TimeTrackerControls>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -12,7 +12,7 @@ function openDesktopGithubRepo() {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="py-4 hidden lg:block" v-if="showReleaseInfo">
|
||||
<div v-if="showReleaseInfo" class="py-4 hidden lg:block">
|
||||
<div
|
||||
class="rounded-lg px-2.5 py-2 bg-card-background border border-border-secondary">
|
||||
<div class="flex items-start justify-between">
|
||||
@@ -23,8 +23,8 @@ function openDesktopGithubRepo() {
|
||||
</div>
|
||||
<button>
|
||||
<XMarkIcon
|
||||
@click="showReleaseInfo = false"
|
||||
class="w-3.5 text-text-tertiary hover:text-text-secondary"></XMarkIcon>
|
||||
class="w-3.5 text-text-tertiary hover:text-text-secondary"
|
||||
@click="showReleaseInfo = false"></XMarkIcon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -34,9 +34,9 @@ function openDesktopGithubRepo() {
|
||||
now.
|
||||
</p>
|
||||
<SecondaryButton
|
||||
@click="openDesktopGithubRepo"
|
||||
size="small"
|
||||
class="w-full text-center justify-center mt-1.5"
|
||||
@click="openDesktopGithubRepo"
|
||||
>Download now</SecondaryButton
|
||||
>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user