add archiving for clients, fixes ST-279

This commit is contained in:
Gregor Vostrak
2024-09-06 16:27:37 +02:00
committed by Constantin Graf
parent 5592d87cd5
commit 19cc05140a
8 changed files with 75 additions and 10 deletions

View File

@@ -118,7 +118,7 @@ const showFreeUpgradeBanner = computed(
<span>Upgrade now</span>
</div>
</Link>
<button @click="hideTrialBanner = true" class="p-1">
<button @click="hideFreeUpgradeBanner = true" class="p-1">
<XMarkIcon
class="w-4 opacity-50 hover:opacity-100"></XMarkIcon>
</button>

View File

@@ -7,6 +7,7 @@ import type { CreateClientBody } from '@/packages/api/src';
import PrimaryButton from '@/packages/ui/src/Buttons/PrimaryButton.vue';
import { useFocus } from '@vueuse/core';
import { useClientsStore } from '@/utils/useClients';
import InputLabel from '../../../packages/ui/src/Input/InputLabel.vue';
const { createClient } = useClientsStore();
const show = defineModel('show', { default: false });
@@ -36,6 +37,7 @@ useFocus(clientNameInput, { initialValue: true });
<template #content>
<div class="flex items-center space-x-4">
<div class="col-span-6 sm:col-span-4 flex-1">
<InputLabel for="clientName" value="Client Name" />
<TextInput
id="clientName"
ref="clientNameInput"

View File

@@ -1,5 +1,9 @@
<script setup lang="ts">
import { PencilSquareIcon, TrashIcon } from '@heroicons/vue/20/solid';
import {
ArchiveBoxIcon,
PencilSquareIcon,
TrashIcon,
} from '@heroicons/vue/20/solid';
import type { Client } from '@/packages/api/src';
import { canDeleteClients, canUpdateClients } from '@/utils/permissions';
import MoreOptionsDropdown from '@/packages/ui/src/MoreOptionsDropdown.vue';
@@ -7,6 +11,7 @@ import MoreOptionsDropdown from '@/packages/ui/src/MoreOptionsDropdown.vue';
const emit = defineEmits<{
delete: [];
edit: [];
archive: [];
}>();
const props = defineProps<{
client: Client;
@@ -26,6 +31,14 @@ const props = defineProps<{
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">
<ArchiveBoxIcon class="w-5 text-icon-active"></ArchiveBoxIcon>
<span>{{ client.is_archived ? 'Unarchive' : 'Archive' }}</span>
</button>
<button
v-if="canDeleteClients()"
@click="emit('delete')"

View File

@@ -3,15 +3,15 @@ import SecondaryButton from '@/packages/ui/src/Buttons/SecondaryButton.vue';
import { UserCircleIcon } from '@heroicons/vue/24/solid';
import { PlusIcon } from '@heroicons/vue/16/solid';
import { type Component, ref } from 'vue';
import { storeToRefs } from 'pinia';
import { useClientsStore } from '@/utils/useClients';
import { type Client } from '@/packages/api/src';
import ClientTableRow from '@/Components/Common/Client/ClientTableRow.vue';
import ClientCreateModal from '@/Components/Common/Client/ClientCreateModal.vue';
import ClientTableHeading from '@/Components/Common/Client/ClientTableHeading.vue';
import { canCreateClients } from '@/utils/permissions';
const { clients } = storeToRefs(useClientsStore());
defineProps<{
clients: Client[];
}>();
const createClient = ref(false);
</script>

View File

@@ -25,6 +25,13 @@ const projectCount = computed(() => {
).length;
});
function archiveClient() {
useClientsStore().updateClient(props.client.id, {
...props.client,
is_archived: !props.client.is_archived,
});
}
const showEditModal = ref(false);
</script>
@@ -50,6 +57,7 @@ const showEditModal = ref(false);
<ClientMoreOptionsDropdown
:client="client"
@edit="showEditModal = true"
@archive="archiveClient"
@delete="deleteClient"></ClientMoreOptionsDropdown>
</div>
</TableRow>

View File

@@ -1,27 +1,62 @@
<script setup lang="ts">
import MainContainer from '@/packages/ui/src/MainContainer.vue';
import AppLayout from '@/Layouts/AppLayout.vue';
import { PlusIcon } from '@heroicons/vue/16/solid';
import { FolderIcon, PlusIcon } from '@heroicons/vue/16/solid';
import SecondaryButton from '@/packages/ui/src/Buttons/SecondaryButton.vue';
import { UserCircleIcon } from '@heroicons/vue/20/solid';
import { onMounted, ref } from 'vue';
import { computed, onMounted, ref } from 'vue';
import { useClientsStore } from '@/utils/useClients';
import ClientTable from '@/Components/Common/Client/ClientTable.vue';
import ClientCreateModal from '@/Components/Common/Client/ClientCreateModal.vue';
import PageTitle from '@/Components/Common/PageTitle.vue';
import { canCreateClients } from '@/utils/permissions';
import TabBarItem from '@/Components/Common/TabBar/TabBarItem.vue';
import TabBar from '@/Components/Common/TabBar/TabBar.vue';
import { storeToRefs } from 'pinia';
onMounted(() => {
useClientsStore().fetchClients();
});
const activeTab = ref<'active' | 'archived'>('active');
function isActiveTab(tab: string) {
return activeTab.value === tab;
}
const createClient = ref(false);
const { clients } = storeToRefs(useClientsStore());
const shownClients = computed(() => {
return clients.value.filter((client) => {
if (activeTab.value === 'active') {
return !client.is_archived;
}
return client.is_archived;
});
});
</script>
<template>
<AppLayout title="Clients" data-testid="clients_view">
<MainContainer
class="py-5 border-b border-default-background-separator flex justify-between items-center">
<div class="flex items-center space-x-3 sm:space-x-6">
<PageTitle :icon="FolderIcon" title="Projects"></PageTitle>
<TabBar>
<TabBarItem
:active="isActiveTab('active')"
@click="activeTab = 'active'"
>Active</TabBarItem
>
<TabBarItem
:active="isActiveTab('archived')"
@click="activeTab = 'archived'">
Archived
</TabBarItem>
</TabBar>
</div>
<div class="flex items-center space-x-6">
<PageTitle :icon="UserCircleIcon" title="Clients"> </PageTitle>
</div>
@@ -33,6 +68,6 @@ const createClient = ref(false);
>
<ClientCreateModal v-model:show="createClient"></ClientCreateModal>
</MainContainer>
<ClientTable></ClientTable>
<ClientTable :clients="shownClients"></ClientTable>
</AppLayout>
</template>

View File

@@ -29,6 +29,10 @@ const props = defineProps<{
currency: string;
}>();
const activeClients = computed(() => {
return props.clients.filter((client) => !client.is_archived);
});
const project = ref<CreateProjectBody>({
name: '',
color: getRandomColor(),
@@ -100,7 +104,7 @@ const currentClientName = computed(() => {
<InputLabel for="client" value="Client" />
<ClientDropdown
:createClient="createClient"
:clients="clients"
:clients="activeClients"
class="mt-2"
v-model="project.client_id">
<template #trigger>

View File

@@ -20,6 +20,9 @@ export const useClientsStore = defineStore('clients', () => {
clientResponse.value = await handleApiRequestNotifications(
() =>
api.getClients({
queries: {
archived: 'all',
},
params: {
organization: organization,
},