KanbanBoardEditDrawer.vue 9.29 KB
<script setup>
import { Placeholder } from '@tiptap/extension-placeholder'
import { TextAlign } from '@tiptap/extension-text-align'
import { Underline } from '@tiptap/extension-underline'
import { StarterKit } from '@tiptap/starter-kit'
import {
  EditorContent,
  useEditor,
} from '@tiptap/vue-3'
import { PerfectScrollbar } from 'vue3-perfect-scrollbar'
import { VForm } from 'vuetify/components/VForm'
import avatar1 from '@images/avatars/avatar-1.png'
import avatar2 from '@images/avatars/avatar-2.png'
import avatar3 from '@images/avatars/avatar-3.png'
import avatar4 from '@images/avatars/avatar-4.png'
import avatar5 from '@images/avatars/avatar-5.png'
import avatar6 from '@images/avatars/avatar-6.png'

const props = defineProps({
  kanbanItem: {
    type: null,
    required: false,
    default: () => ({
      item: {
        title: '',
        dueDate: '2022-01-01T00:00:00Z',
        labels: [],
        members: [],
        id: 0,
        attachments: 0,
        commentsCount: 0,
        image: '',
        comments: '',
      },
      boardId: 0,
      boardName: '',
    }),
  },
  isDrawerOpen: {
    type: Boolean,
    required: true,
  },
})

const emit = defineEmits([
  'update:isDrawerOpen',
  'update:kanbanItem',
  'deleteKanbanItem',
])

const refEditTaskForm = ref()

const labelOptions = [
  'UX',
  'Image',
  'Code Review',
  'Dashboard',
  'App',
  'Charts & Maps',
]

const localKanbanItem = ref(JSON.parse(JSON.stringify(props.kanbanItem.item)))

const handleDrawerModelValueUpdate = val => {
  emit('update:isDrawerOpen', val)
  if (!val)
    refEditTaskForm.value?.reset()
}

// kanban item watcher
watch(() => props.kanbanItem, () => {
  localKanbanItem.value = JSON.parse(JSON.stringify(props.kanbanItem.item))
}, { deep: true })

const updateKanbanItem = () => {
  refEditTaskForm.value?.validate().then(async valid => {
    if (valid.valid) {
      emit('update:kanbanItem', {
        item: localKanbanItem.value,
        boardId: props.kanbanItem.boardId,
        boardName: props.kanbanItem.boardName,
      })
      emit('update:isDrawerOpen', false)
      await nextTick()
      refEditTaskForm.value?.reset()
    }
  })
}

// delete kanban item
const deleteKanbanItem = () => {
  emit('deleteKanbanItem', {
    item: localKanbanItem.value,
    boardId: props.kanbanItem.boardId,
    boardName: props.kanbanItem.boardName,
  })
  emit('update:isDrawerOpen', false)
}

// 👉 label/chip color
const resolveLabelColor = {
  'UX': 'success',
  'Image': 'warning',
  'Code Review': 'error',
  'Dashboard': 'info',
  'App': 'secondary',
  'Charts & Maps': 'primary',
}

const editor = useEditor({
  content: '',
  extensions: [
    StarterKit,
    TextAlign.configure({
      types: [
        'heading',
        'paragraph',
      ],
    }),
    Placeholder.configure({ placeholder: 'Write a Comment...' }),
    Underline,
  ],
})

const config = ref({
  altFormat: 'j M, Y',
  altInput: true,
  dateFormat: 'Y-m-d',
})

const users = [
  {
    img: avatar1,
    name: 'John Doe',
  },
  {
    img: avatar2,
    name: 'Jane Smith',
  },
  {
    img: avatar3,
    name: 'Robert Johnson',
  },
  {
    img: avatar4,
    name: 'Lucy Brown',
  },
  {
    img: avatar5,
    name: 'Mike White',
  },
  {
    img: avatar6,
    name: 'Anna Black',
  },
]

const fileAttached = ref()
</script>

<template>
  <VNavigationDrawer
    location="end"
    :width="370"
    temporary
    border="0"
    :model-value="props.isDrawerOpen"
    @update:model-value="handleDrawerModelValueUpdate"
  >
    <!-- 👉 Header -->
    <AppDrawerHeaderSection
      title="Edit Task"
      @cancel="$emit('update:isDrawerOpen', false)"
    />

    <VDivider />

    <PerfectScrollbar
      :options="{ wheelPropagation: false }"
      style="block-size: calc(100vh - 4rem);"
    >
      <VForm
        v-if="localKanbanItem"
        ref="refEditTaskForm"
        @submit.prevent="updateKanbanItem"
      >
        <VCardText class="kanban-editor-drawer">
          <VRow>
            <VCol cols="12">
              <VTextField
                v-model="localKanbanItem.title"
                label="Title"
                :rules="[requiredValidator]"
              />
            </VCol>

            <VCol cols="12">
              <AppDateTimePicker
                v-model="localKanbanItem.dueDate"
                label="Due date"
                :config="config"
              />
            </VCol>

            <VCol cols="12">
              <VSelect
                v-model="localKanbanItem.labels"
                :items="labelOptions"
                label="Label"
                multiple
                eager
              >
                <template #chip="{ item }">
                  <VChip :color="resolveLabelColor[item.raw]">
                    {{ item.raw }}
                  </VChip>
                </template>
              </VSelect>
            </VCol>

            <VCol cols="12">
              <p class="mb-0 text-sm text-medium-emphasis">
                Assigned
              </p>

              <div>
                <VSelect
                  v-model="localKanbanItem.members"
                  density="compact"
                  :items="users"
                  item-title="name"
                  item-value="name"
                  multiple
                  return-object
                  variant="plain"
                  :menu-props="{
                    offset: 10,
                  }"
                  class="assignee-select"
                >
                  <template #selection="{ item }">
                    <VAvatar size="26">
                      <VImg :src="item.raw.img" />

                      <VTooltip activator="parent">
                        {{ item.raw.name }}
                      </VTooltip>
                    </VAvatar>
                  </template>

                  <template #prepend-inner>
                    <IconBtn
                      size="26"
                      variant="tonal"
                      class="mt-1"
                    >
                      <VIcon
                        size="20"
                        icon="ri-add-line"
                      />
                    </IconBtn>
                  </template>
                </VSelect>
              </div>
            </VCol>

            <VCol cols="12">
              <VFileInput
                v-model="fileAttached"
                label="Choose file"
                multiple
              />
            </VCol>

            <VCol cols="12">
              <p
                class="text-body-2 text-high-emphasis mb-1"
                style="line-height: 15px;"
              >
                COMMENT
              </p>
              <div class="border rounded px-3 py-2">
                <EditorContent :editor="editor" />
                <div
                  v-if="editor"
                  class="d-flex justify-end flex-wrap gap-x-2"
                >
                  <VIcon
                    icon="ri-bold"
                    :color="editor.isActive('bold') ? 'primary' : 'secondary'"
                    size="20"
                    @click="editor.chain().focus().toggleBold().run()"
                  />

                  <VIcon
                    :color="editor.isActive('underline') ? 'primary' : 'secondary'"
                    icon="ri-underline"
                    size="20"
                    @click="editor.commands.toggleUnderline()"
                  />

                  <VIcon
                    :color="editor.isActive('italic') ? 'primary' : 'secondary'"
                    icon="ri-italic"
                    size="20"
                    @click="editor.chain().focus().toggleItalic().run()"
                  />

                  <VIcon
                    :color="editor.isActive({ textAlign: 'left' }) ? 'primary' : 'secondary'"
                    icon="ri-align-left"
                    size="20"
                    @click="editor.chain().focus().setTextAlign('left').run()"
                  />

                  <VIcon
                    :color="editor.isActive({ textAlign: 'center' }) ? 'primary' : 'secondary'"
                    icon="ri-align-center"
                    size="20"
                    @click="editor.chain().focus().setTextAlign('center').run()"
                  />

                  <VIcon
                    :color="editor.isActive({ textAlign: 'right' }) ? 'primary' : 'secondary'"
                    icon="ri-align-right"
                    size="20"
                    @click="editor.chain().focus().setTextAlign('right').run()"
                  />
                </div>
              </div>
            </VCol>

            <VCol cols="12">
              <VBtn
                type="submit"
                class="me-4"
              >
                Update
              </VBtn>
              <VBtn
                color="error"
                variant="tonal"
                @click="deleteKanbanItem"
              >
                Delete
              </VBtn>
            </VCol>
          </VRow>
        </VCardText>
      </VForm>
    </PerfectScrollbar>
  </VNavigationDrawer>
</template>

<style lang="scss">
.kanban-editor-drawer {
  .assignee-select {
    .v-field__append-inner {
      .v-select__menu-icon {
        display: none;
      }
    }
  }

  .ProseMirror {
    padding: 0;
    min-block-size: 7vh !important;

    p {
      margin-block-end: 0;
    }
  }

  .ProseMirror-focused {
    outline: none !important;
  }
}
</style>