chore: refactor issue component

This commit is contained in:
Muhsin Keloth
2024-05-20 10:31:37 +05:30
parent 4263ea7db1
commit b5d655171e
4 changed files with 118 additions and 137 deletions
@@ -1,7 +1,7 @@
<template>
<div @submit.prevent="onSubmit">
<woot-input
v-model="state.title"
v-model="formState.title"
:class="{ error: v$.title.$error }"
class="w-full"
:styles="{ ...inputStyles, padding: '6px 12px' }"
@@ -15,10 +15,9 @@
<label>
{{ $t('INTEGRATION_SETTINGS.LINEAR.ADD_OR_LINK.FORM.DESCRIPTION.LABEL') }}
<textarea
v-model="state.description"
v-model="formState.description"
:style="{ ...inputStyles, padding: '8px 12px' }"
rows="3"
type="text"
class="text-sm"
:placeholder="
$t(
@@ -30,9 +29,9 @@
<label :class="{ error: v$.teamId.$error }">
{{ $t('INTEGRATION_SETTINGS.LINEAR.ADD_OR_LINK.FORM.TEAM.LABEL') }}
<select
v-model="state.teamId"
v-model="formState.teamId"
:style="inputStyles"
@change="onChangeTeam($event)"
@change="onChangeTeam"
>
<option v-for="item in teams" :key="item.name" :value="item.id">
{{ item.name }}
@@ -44,49 +43,44 @@
</label>
<label>
{{ $t('INTEGRATION_SETTINGS.LINEAR.ADD_OR_LINK.FORM.ASSIGNEE.LABEL') }}
<select v-model="state.assigneeId" :style="inputStyles">
<select v-model="formState.assigneeId" :style="inputStyles">
<option v-for="item in assignees" :key="item.name" :value="item.id">
{{ item.name }}
</option>
</select>
</label>
<label>
{{ $t('INTEGRATION_SETTINGS.LINEAR.ADD_OR_LINK.FORM.LABEL.LABEL') }}
<select v-model="state.labelId" :style="inputStyles">
<select v-model="formState.labelId" :style="inputStyles">
<option v-for="item in labels" :key="item.name" :value="item.id">
{{ item.name }}
</option>
</select>
</label>
<label>
{{ $t('INTEGRATION_SETTINGS.LINEAR.ADD_OR_LINK.FORM.PRIORITY.LABEL') }}
<select v-model="state.priority" :style="inputStyles">
<select v-model="formState.priority" :style="inputStyles">
<option v-for="item in priorities" :key="item.name" :value="item.id">
{{ item.name }}
</option>
</select>
</label>
<label>
{{ $t('INTEGRATION_SETTINGS.LINEAR.ADD_OR_LINK.FORM.PROJECT.LABEL') }}
<select v-model="state.projectId" :style="inputStyles">
<select v-model="formState.projectId" :style="inputStyles">
<option v-for="item in projects" :key="item.name" :value="item.id">
{{ item.name }}
</option>
</select>
</label>
<label>
{{ $t('INTEGRATION_SETTINGS.LINEAR.ADD_OR_LINK.FORM.STATUS.LABEL') }}
<select v-model="state.stateId" :style="inputStyles">
<select v-model="formState.stateId" :style="inputStyles">
<option v-for="item in statuses" :key="item.name" :value="item.id">
{{ item.name }}
</option>
</select>
</label>
<div class="flex items-center justify-end w-full gap-2 mt-8">
<woot-button
class="px-4 rounded-xl button clear outline-woot-200/50 outline"
@@ -105,12 +99,13 @@
</div>
</div>
</template>
<script setup>
import LinearAPI from 'dashboard/api/integrations/linear';
import { reactive, computed, onMounted, ref } from 'vue';
import { useVuelidate } from '@vuelidate/core';
import { useI18n } from 'dashboard/composables/useI18n';
import { useAlert } from 'dashboard/composables';
import LinearAPI from 'dashboard/api/integrations/linear';
import validations from './validations';
const props = defineProps({
@@ -124,6 +119,7 @@ const props = defineProps({
},
});
const emit = defineEmits(['close']);
const { t } = useI18n();
const teams = ref([]);
@@ -133,37 +129,17 @@ const labels = ref([]);
const statuses = ref([]);
const priorities = [
{
id: 0,
name: 'No priority',
},
{
id: 1,
name: 'Urgent',
},
{
id: 2,
name: 'High',
},
{
id: 3,
name: 'Normal',
},
{
id: 4,
name: 'Low',
},
{ id: 0, name: 'No priority' },
{ id: 1, name: 'Urgent' },
{ id: 2, name: 'High' },
{ id: 3, name: 'Normal' },
{ id: 4, name: 'Low' },
];
const isCreating = ref(false);
const inputStyles = { borderRadius: '12px', fontSize: '14px' };
const inputStyles = {
borderRadius: '12px',
fontSize: '14px',
};
const emit = defineEmits(['close']);
const state = reactive({
const formState = reactive({
title: '',
description: '',
teamId: '',
@@ -174,27 +150,24 @@ const state = reactive({
projectId: '',
});
const v$ = useVuelidate(validations, state);
const v$ = useVuelidate(validations, formState);
const isSubmitDisabled = computed(() => {
return v$.value.title.$invalid || isCreating.value;
});
const nameError = computed(() => {
return v$.value.title.$error
const isSubmitDisabled = computed(
() => v$.value.title.$invalid || isCreating.value
);
const nameError = computed(() =>
v$.value.title.$error
? t('INTEGRATION_SETTINGS.LINEAR.ADD_OR_LINK.FORM.TITLE.REQUIRED_ERROR')
: '';
});
const teamError = computed(() => {
return v$.value.teamId.$error
: ''
);
const teamError = computed(() =>
v$.value.teamId.$error
? t('INTEGRATION_SETTINGS.LINEAR.ADD_OR_LINK.FORM.TEAM.REQUIRED_ERROR')
: '';
});
: ''
);
const onClose = () => emit('close');
const onClose = () => {
emit('close');
};
const getTeams = async () => {
try {
const response = await LinearAPI.getTeams();
@@ -206,7 +179,7 @@ const getTeams = async () => {
const getTeamEntities = async () => {
try {
const response = await LinearAPI.getTeamEntities(state.teamId);
const response = await LinearAPI.getTeamEntities(formState.teamId);
assignees.value = response.data.users;
labels.value = response.data.labels;
statuses.value = response.data.states;
@@ -219,40 +192,28 @@ const getTeamEntities = async () => {
};
const onChangeTeam = event => {
state.teamId = event.target.value;
state.assigneeId = '';
state.stateId = '';
state.labelId = '';
formState.teamId = event.target.value;
formState.assigneeId = '';
formState.stateId = '';
formState.labelId = '';
getTeamEntities();
};
const createIssue = async () => {
v$.value.$touch();
if (v$.value.$invalid) {
return;
}
if (v$.value.$invalid) return;
const payload = {
team_id: state.teamId,
title: state.title,
team_id: formState.teamId,
title: formState.title,
description: formState.description || undefined,
assignee_id: formState.assigneeId || undefined,
project_id: formState.projectId || undefined,
state_id: formState.stateId || undefined,
priority: formState.priority || undefined,
label_ids: formState.labelId ? [formState.labelId] : undefined,
};
if (state.description) {
payload.description = state.description;
}
if (state.assigneeId) {
payload.assignee_id = state.assigneeId;
}
if (state.projectId) {
payload.project_id = state.projectId;
}
if (state.stateId) {
payload.state_id = state.stateId;
}
if (state.priority) {
payload.priority = state.priority;
}
if (state.labelId) {
payload.label_ids = [state.labelId];
}
try {
isCreating.value = true;
const response = await LinearAPI.createIssue(payload);
@@ -267,7 +228,5 @@ const createIssue = async () => {
}
};
onMounted(() => {
getTeams();
});
onMounted(getTeams);
</script>
@@ -1,6 +1,7 @@
<script setup>
import { format } from 'date-fns';
import UserAvatarWithName from 'dashboard/components/widgets/UserAvatarWithName.vue';
import IssueHeader from './IssueHeader.vue';
import { computed } from 'vue';
const priorityMap = {
@@ -47,55 +48,19 @@ const priorityLabel = computed(() => {
const unlinkIssue = () => {
emit('unlink-issue', props.linkId);
};
const openIssue = () => {
window.open(props.issue.url, '_blank');
};
</script>
<template>
<div
class="absolute flex flex-col items-start bg-[#fdfdfd] dark:bg-slate-800 z-50 px-4 py-3 border border-solid border-slate-75 dark:border-slate-700 w-[384px] rounded-xl gap-4 max-h-96 overflow-auto"
class="absolute flex flex-col items-start bg-ash-50 dark:bg-slate-800 z-50 px-4 py-3 border border-solid border-ash-200 w-[384px] rounded-xl gap-4 max-h-96 overflow-auto"
>
<div class="flex flex-col w-full">
<div class="flex flex-row justify-between">
<div
class="flex items-center justify-center gap-1 h-[24px] px-2 py-1 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>
<div class="flex items-center gap-0.5">
<woot-button
variant="clear"
color-scheme="secondary"
class="h-[24px]"
@click="unlinkIssue"
>
<fluent-icon
icon="unlink"
size="12"
type="outline"
icon-lib="lucide"
/>
</woot-button>
<woot-button
variant="clear"
class="h-[24px]"
color-scheme="secondary"
@click="openIssue"
>
<fluent-icon icon="arrow-up-right" size="14" />
</woot-button>
</div>
</div>
<issue-header
:identifier="issue.identifier"
:link-id="linkId"
:issue-url="issue.url"
@unlink-issue="unlinkIssue"
/>
<span class="mt-2 text-sm font-medium text-ash-900">
{{ issue.title }}
@@ -109,14 +74,14 @@ const openIssue = () => {
</div>
<div class="flex flex-row items-center h-6 gap-2">
<user-avatar-with-name v-if="assignee" :user="assignee" class="py-1" />
<div class="w-px h-3 bg-ash-200" />
<div v-if="assignee" class="w-px h-3 bg-ash-200" />
<div class="flex items-center gap-1 py-1">
<fluent-icon
icon="status"
size="14"
:style="{ color: issue.state.color }"
/>
<h6 class="text-xs text-slate-600">
<h6 class="text-xs text-ash-900">
{{ issue.state.name }}
</h6>
</div>
@@ -127,7 +92,7 @@ const openIssue = () => {
size="14"
view-box="0 0 12 12"
/>
<h6 class="text-xs text-slate-600">{{ priorityLabel }}</h6>
<h6 class="text-xs text-ash-900">{{ priorityLabel }}</h6>
</div>
</div>
<div v-if="labels.length" class="flex flex-wrap items-center gap-1">
@@ -0,0 +1,56 @@
<template>
<div class="flex flex-row justify-between">
<div
class="flex items-center justify-center gap-1 h-[24px] px-2 py-1 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">{{ identifier }}</span>
</div>
<div class="flex items-center gap-0.5">
<woot-button
variant="clear"
color-scheme="secondary"
class="h-[24px]"
@click="unlinkIssue"
>
<fluent-icon icon="unlink" size="12" type="outline" icon-lib="lucide" />
</woot-button>
<woot-button
variant="clear"
class="h-[24px]"
color-scheme="secondary"
@click="openIssue"
>
<fluent-icon icon="arrow-up-right" size="14" />
</woot-button>
</div>
</div>
</template>
<script setup>
const props = defineProps({
identifier: {
type: String,
required: true,
},
issueUrl: {
type: String,
required: true,
},
});
const emit = defineEmits(['unlink-issue']);
const unlinkIssue = () => {
emit('unlink-issue');
};
const openIssue = () => {
window.open(props.issueUrl, '_blank');
};
</script>
@@ -111,6 +111,7 @@ const linkIssue = async () => {
await LinearAPI.link_issue(props.conversationId, issueId);
useAlert(t('INTEGRATION_SETTINGS.LINEAR.LINK.LINK_SUCCESS'));
searchQuery.value = '';
items.value = [];
onClose();
} catch (error) {
useAlert(t('INTEGRATION_SETTINGS.LINEAR.LINK.LINK_ERROR'));