feat: create basic front end

This commit is contained in:
Muhsin Keloth
2024-05-17 12:06:19 +05:30
parent e992c23d03
commit 0e11dac471
12 changed files with 157 additions and 69 deletions
@@ -71,6 +71,10 @@
:class="{ 'justify-end': isContactPanelOpen }"
>
<SLA-card-label v-if="hasSlaPolicyId" :chat="chat" show-extended-info />
<linear
v-if="isLinearIntegrationEnabled"
:conversation-id="currentChat.id"
/>
<more-actions :conversation-id="currentChat.id" />
</div>
</div>
@@ -89,6 +93,8 @@ import SLACardLabel from './components/SLACardLabel.vue';
import wootConstants from 'dashboard/constants/globals';
import { conversationListPageURL } from 'dashboard/helper/URLHelper';
import { snoozedReopenTime } from 'dashboard/helper/snoozeHelpers';
import { FEATURE_FLAGS } from 'dashboard/featureFlags';
import Linear from './linear/index.vue';
export default {
components: {
@@ -97,6 +103,7 @@ export default {
MoreActions,
Thumbnail,
SLACardLabel,
Linear,
},
mixins: [inboxMixin, agentMixin, keyboardEventListenerMixins],
props: {
@@ -121,6 +128,8 @@ export default {
...mapGetters({
uiFlags: 'inboxAssignableAgents/getUIFlags',
currentChat: 'getSelectedChat',
accountId: 'getCurrentAccountId',
isFeatureEnabledonAccount: 'accounts/isFeatureEnabledonAccount',
}),
chatMetadata() {
return this.chat.meta;
@@ -178,6 +187,12 @@ export default {
hasSlaPolicyId() {
return this.chat?.sla_policy_id;
},
isLinearIntegrationEnabled() {
return this.isFeatureEnabledonAccount(
this.accountId,
FEATURE_FLAGS.LINEAR
);
},
},
methods: {
@@ -21,9 +21,9 @@
</woot-button>
</div>
<span class="text-sm font-normal text-slate-900 dark:text-slate-25">
<!-- <span class="text-sm font-normal text-slate-900 dark:text-slate-25">
{{ issue.description }}
</span>
</span> -->
</div>
<div class="flex justify-between w-full">
@@ -0,0 +1,100 @@
<script setup>
import { format } from 'date-fns';
import UserAvatarWithName from 'dashboard/components/widgets/UserAvatarWithName.vue';
import { computed } from 'vue';
const priorityMap = {
0: 'Low',
1: 'Medium',
2: 'High',
3: 'Urgent',
};
const props = defineProps({
issue: {
type: Object,
required: true,
},
});
const formattedDate = computed(() => {
return format(new Date(props.issue.createdAt), 'hh:mm a, MMM dd');
});
const assignee = computed(() => {
if (!props.issue.assignee) {
return null;
}
return {
name: props.issue.assignee.name,
thumbnail: props.issue.assignee.avatarUrl,
};
});
const labels = computed(() => {
if (props.issue.labels.nodes.length) {
return props.issue.labels.nodes;
}
return [];
});
const priorityLabel = computed(() => {
return priorityMap[props.issue.priority];
});
</script>
<template>
<div
class="absolute flex flex-col items-start bg-[#fdfdfd] dark:bg-slate-800 z-50 p-4 border border-solid border-slate-75 dark:border-slate-700 w-[384px] rounded-xl gap-4 max-h-96 overflow-auto"
>
<div class="flex flex-col items-start gap-2">
<div
class="flex items-center justify-center gap-1 px-2 py-1 font-medium border rounded-lg border-ash-200"
>
<fluent-icon
icon="linear"
size="19"
class="text-[#5E6AD2]"
view-box="0 0 19 19"
/>
<span class="text-xs font-medium text-ash-900">
{{ issue.identifier }}
</span>
</div>
<span class="text-sm font-medium text-ash-900">
{{ issue.title }}
</span>
<span class="text-sm text-ash-800">
{{
issue.description.length > 130
? issue.description.slice(0, 130) + '...'
: issue.description
}}
</span>
</div>
<div class="flex flex-row gap-2 divide-x divide-ash-200">
<user-avatar-with-name v-if="assignee" :user="assignee" />
<div class="flex items-center gap-1 px-2">
<fluent-icon icon="status" size="14" />
<h6 class="my-0 text-xs text-slate-600">{{ issue.state.name }}</h6>
</div>
<div class="flex items-center gap-1 px-2">
<fluent-icon icon="priority-high" size="14" view-box="0 0 14 14" />
<h6 class="my-0 text-xs text-slate-600">{{ priorityLabel }}</h6>
</div>
</div>
<div v-if="labels.length" class="flex items-center gap-1">
<woot-label
v-for="label in labels"
:key="label.id"
:title="label.name"
:description="label.description"
:color="label.color"
variant="smooth"
small
/>
</div>
<div class="flex items-center">
<span class="text-xs text-ash-800">
{{ `Created at ${formattedDate}` }}
</span>
</div>
</div>
</template>
@@ -1,30 +1,23 @@
<template>
<div class="flex flex-col">
<div class="flex flex-row gap-2">
<woot-button
v-if="shouldShowAddButton"
variant="smooth"
icon="add"
size="tiny"
@click="showCreateIssuePopup"
>
{{ $t('INTEGRATION_SETTINGS.LINEAR.ADD_OR_LINK_BUTTON') }}
</woot-button>
</div>
<div v-if="isFetching" class="flex items-center justify-center">
<span class="text-sm font-medium text-slate-800 dark:text-slate-100">
{{ $t('INTEGRATION_SETTINGS.LINEAR.LOADING') }}
</span>
</div>
<div v-if="shouldShowItem" class="flex flex-col gap-2">
<issue-item
:issue="linkedIssue.issue"
:link-id="linkedIssue.id"
:conversation-id="conversationId"
@unlink-issue="unlinkIssue"
<div class="relative" @mouseover="openSlaPopover()">
<woot-button
v-on-clickaway="closeSlaPopover"
variant="clear"
color-scheme="secondary"
@click="openSlaPopover()"
>
<fluent-icon
icon="linear"
size="19"
class="text-[#5E6AD2]"
view-box="0 0 19 19"
/>
</div>
</woot-button>
<linear-issue-details
v-if="showSlaPopoverCard"
:issue="linkedIssue.issue"
class="absolute right-0 top-[46px]"
/>
<woot-modal
:show.sync="showPopup"
:on-close="closePopup"
@@ -38,20 +31,18 @@
</woot-modal>
</div>
</template>
<script>
import { mapGetters } from 'vuex';
import IssueItem from './IssueItem.vue';
import accountMixin from 'dashboard/mixins/account.js';
import alertMixin from 'shared/mixins/alertMixin';
import CreateOrLinkIssue from './CreateOrLinkIssue.vue';
import LinearIssueDetails from './LinearIssueDetails.vue';
import LinearAPI from 'dashboard/api/integrations/linear';
import CreateOrLinkIssue from './CreateOrLinkIssue.vue';
export default {
components: {
IssueItem,
LinearIssueDetails,
CreateOrLinkIssue,
},
mixins: [accountMixin, alertMixin],
props: {
conversationId: {
type: [Number, String],
@@ -60,20 +51,17 @@ export default {
},
data() {
return {
isFetching: false,
showPopup: false,
linkedIssue: null,
showSlaPopover: false,
showPopup: false,
};
},
computed: {
...mapGetters({
currentAccountId: 'getCurrentAccountId',
}),
shouldShowAddButton() {
return !this.isFetching && !this.linkedIssue;
},
shouldShowItem() {
return !this.isFetching && this.linkedIssue;
showSlaPopoverCard() {
return this.showSlaPopover && this.linkedIssue;
},
},
watch: {
@@ -87,23 +75,17 @@ export default {
this.loadLinkedIssue();
},
methods: {
showCreateIssuePopup() {
this.showPopup = true;
},
closePopup() {
this.showPopup = false;
this.loadLinkedIssue();
},
async loadLinkedIssue() {
try {
this.isFetching = true;
const response = await LinearAPI.getLinkedIssue(this.conversationId);
const issues = response.data;
this.linkedIssue = issues && issues.length ? issues[0] : null;
} catch (error) {
this.showAlert(this.$t('INTEGRATION_SETTINGS.LINEAR.LOADING_ERROR'));
} finally {
this.isFetching = false;
}
},
async unlinkIssue(linkId) {
@@ -117,6 +99,13 @@ export default {
);
}
},
openSlaPopover() {
if (!this.linkedIssue) this.showPopup = true;
this.showSlaPopover = true;
},
closeSlaPopover() {
this.showSlaPopover = false;
},
},
};
</script>
@@ -23,18 +23,6 @@
:key="element.name"
class="bg-white dark:bg-gray-800"
>
<div
v-if="element.name === 'linear' && isLinearIntegrationEnabled"
class="conversation--actions"
>
<accordion-item
title="Linear"
:is-open="isContactSidebarItemOpen('is_linear_open')"
@click="value => toggleSidebarUIState('is_linear_open', value)"
>
<linear :conversation-id="conversationId" />
</accordion-item>
</div>
<div
v-if="element.name === 'conversation_actions'"
class="conversation--actions"
@@ -157,8 +145,6 @@ import CustomAttributes from './customAttributes/CustomAttributes.vue';
import draggable from 'vuedraggable';
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
import MacrosList from './Macros/List.vue';
import { FEATURE_FLAGS } from 'dashboard/featureFlags';
import Linear from './linear/index.vue';
export default {
components: {
AccordionItem,
@@ -170,7 +156,6 @@ export default {
ConversationParticipant,
draggable,
MacrosList,
Linear,
},
mixins: [alertMixin, uiSettingsMixin],
props: {
@@ -226,12 +211,6 @@ export default {
const { custom_attributes: customAttributes } = this.contact;
return customAttributes && Object.keys(customAttributes).length;
},
isLinearIntegrationEnabled() {
return this.isFeatureEnabledonAccount(
this.accountId,
FEATURE_FLAGS.LINEAR
);
},
},
watch: {
conversationId(newConversationId, prevConversationId) {