build a trello clone with nuxt 3

==


<div v-show="isModalOpen" class="task-bg" @click.self="closeModal">
<NuxtPage :key="route.fullPath" />
</div>

=

const isModalOpen = computed(() => {
console.log('sv',route.name);
return route.name === 'tasks-id'
})

=

creud for tasks

https://www.vuemastery.com/courses/build-a-trello-clone-w-nuxt-3/crud-for-tasks

https://github.com/Code-Pop/build-trello-clone-with-nuxt-3/tree/06-end

npm i uuid 

used for to generate unique id

so now we are adding new task to that columns 

ist create input field on every column

thne create functiin when key enter

after it is called store variables

then it saved  to loocal storage


<UInput
v-model="newTaskName"
type="text"
placeholder="Create new task"
icon="i-heroicons-plus-circle-solid"
@keyup.enter="addTask"
/>


=

function addTask() {
boardStore.addTask({
taskName: newTaskName.value,
columnIndex: props.columnIndex
})
newTaskName.value = ''
}

=

function addTask({ columnIndex, taskName }) {
board.value.columns[columnIndex].tasks.push({
id: uuid(),
name: taskName,
description: ''
})
}

=


edit the task in column



<template>
<div class="task-wrapper">
<div class="task-view">
<UInput type="text" v-model="task.name" />
<UTextarea type="text" v-model="task.description" ></UTextarea>

</div>
</div>

to make it beautiful

<UFormGroup label="Name" class="w-full mb-4">
<UInput type="text" v-model="task.name" />
</UFormGroup>
<UFormGroup label="Description" class="w-full mb-4">
<UTextarea v-model="task.description" />
</UFormGroup>

 


now delete task at view


at [id].vue

function deleteTask() {
console.log('va');
boardStore.deleteTask(route.params.id)
router.push('/')
}

=

<UButton icon="i-heroicons-trash" color="red" @click="deleteTask">
Delete task
</UButton>

=

function deleteTask(taskId) {
console.log(taskId);
for (const column of board.value.columns) {
const taskIndex = column.tasks.findIndex(task => task.id === taskId)

if (taskIndex !== -1) {
column.tasks.splice(taskIndex, 1)
return
}
}
}

=

at boardStore.js

import { v4 as uuid } from 'uuid'
import { defineStore } from 'pinia'
import { useStorage } from '@vueuse/core'
import boardData from '~/data/board.json'

export const useBoardStore = defineStore('boardStore', () => {
const board = useStorage('board', boardData)

/**
* Tasks
*/
const getTask = computed(() => {
return taskId => {
for (const column of board.value.columns) {
const task = column.tasks.find(task => task.id === taskId)
if (task) return task
}
}
})

function addTask({ columnIndex, taskName }) {
board.value.columns[columnIndex].tasks.push({
id: uuid(),
name: taskName,
description: ''
})
}

function deleteTask(taskId) {
console.log(taskId);
for (const column of board.value.columns) {
const taskIndex = column.tasks.findIndex(task => task.id === taskId)

if (taskIndex !== -1) {
column.tasks.splice(taskIndex, 1)
return
}
}
}

/**
* Columns
*/
function addColumn(columnName) {
board.value.columns.push({
name: columnName,
tasks: []
})
}

function deleteColumn(columnIndex) {
board.value.columns.splice(columnIndex, 1)
}

return {
/* State */
board,
/* Getters */
getTask,
/* Actions*/
addColumn,
addTask,
deleteColumn,
deleteTask
}
})


at [id].vue

<script setup>
import { useBoardStore } from '~/stores/boardStore'

const boardStore = useBoardStore()
const route = useRoute()
const router = useRouter()
const task = computed(() => {
return boardStore.getTask(route.params.id)
})

function deleteTask() {
console.log('va');
boardStore.deleteTask(route.params.id)
router.push('/')
}
</script>

<template>
<div class="task-wrapper">
<div class="task-view">
<UFormGroup label="Name" class="w-full mb-4">
<UInput type="text" v-model="task.name" />
</UFormGroup>
<UFormGroup label="Description" class="w-full mb-4">
<UTextarea v-model="task.description" />
</UFormGroup>
<UButton icon="i-heroicons-trash" color="red" @click="deleteTask">
Delete task
</UButton>

</div>
</div>
</template>

=

INDEX.VUE

<script setup>
import { useBoardStore } from '../stores/boardStore'

const boardStore = useBoardStore()
const route = useRoute()
const router = useRouter()

const newColumnName = ref('')

const isModalOpen = computed(() => {
console.log('sv',route.name);
return route.name === 'tasks-id'
})


function addColumn() {
boardStore.addColumn(newColumnName.value)
newColumnName.value = ''
}

function closeModal() {
router.push('/')
}
</script>

<template>
<div class="board-wrapper">
<main class="board">
<BoardColumn
v-for="(column, columnIndex) in boardStore.board.columns"
:key="column.id"
:column="column"
:columnIndex="columnIndex"
/>
<UContainer class="column">
<UInput
v-model="newColumnName"
type="text"
placeholder="Create new column"
icon="i-heroicons-plus-circle-solid"
@keyup.enter="addColumn"
/>
</UContainer>
</main>
<div v-show="isModalOpen" class="task-bg" @click.self="closeModal">
<NuxtPage :key="route.fullPath" />
</div>
</div>
</template>

=

<script setup>
import { useBoardStore } from '../stores/boardStore'

const props = defineProps({
column: {
type: Object,
required: true
},
columnIndex: {
type: Number,
required: true
}
})

const boardStore = useBoardStore()
const router = useRouter()

const editNameState = ref(false)
const newTaskName = ref('')

function addTask() {
boardStore.addTask({
taskName: newTaskName.value,
columnIndex: props.columnIndex
})
newTaskName.value = ''
}

function deleteColumn(columnIndex) {
boardStore.deleteColumn(columnIndex)
}

function goToTask(taskId) {
router.push(`/tasks/${taskId}`)
}
</script>

<template>
<UContainer class="column">
<div class="column-header mb-4">
<div>
<UInput v-if="editNameState" type="text" v-model="column.name" />
<h2 v-else>{{ column.name }}</h2>
</div>
<div>
<UButton
icon="i-heroicons-pencil-square"
class="mr-2"
@click="editNameState = !editNameState"
/>
<UButton
icon="i-heroicons-trash"
color="red"
@click="deleteColumn(columnIndex)"
/>
</div>
</div>
<ul>
<li v-for="task in column.tasks" :key="task.id">
<UCard class="mb-4" @click="goToTask(task.id)">
<strong>{{ task.name }}</strong>
<p>{{ task.description }}</p>
</UCard>
</li>
</ul>
<UInput
v-model="newTaskName"
type="text"
placeholder="Create new task"
icon="i-heroicons-plus-circle-solid"
@keyup.enter="addTask"
/>
</UContainer>
</template>

==


DRAG AND dROP TASKS

https://github.com/Code-Pop/build-trello-clone-with-nuxt-3/tree/07-end

https://www.vuemastery.com/courses/build-a-trello-clone-w-nuxt-3/nuxt-drag-and-drop-tasks

https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API

draggable="true"

boardcolumn 

<UCard class="mb-4" @click="goToTask(task.id)" draggable="true">

drag events

drag start, dragstop,dragover,enter,leave

@dragstart="pickUpTask"
@dragend="dragEnd"
@dragenter="dragEnter"
@dragleave="dragLeave"
@dragover="dragOver"
draggable="true"

d


function pickUpTask(event){
console.log(event);
}
function dragStop(event)
{
console.log('dragstop',event);
}
function dragEnter(event)
{
console.log('dragEnter',event);
}
function dragEnd(event)
{
console.log('dragEnd',event);
}
function dragOver(event)
{
console.log('dragover',event);
}
function dragLeave(event)
{
console.log('dragLeave',event);
}

=

@drop="dropTask($event)"
@dragenter.prevent
@dragover.prevent
@dragstop="dropTask($event)"

prevent drag enter

preven drag over

prevent drag stop

self prevent

=

function dropTask(event)
{
const fromColumnIndex=event.dataTransfer.getData('from-column-index');
const fromTaskColumnIndex=event.dataTransfer.getData('from-task-index');
console.log(fromColumnIndex,fromTaskColumnIndex);
}

=

function pickUpTask(event,{fromColumnIndex,fromTaskIndex}){
event.dataTransfer.effectAllower='move';
event.dataTransfer.dropEffect='move';
event.dataTransfer.setData('from-column-index',fromColumnIndex);
event.dataTransfer.setData('from-task-index',fromTaskIndex)
}


=

<UContainer
class="column"
@drop="dropTask($event)"
@dragenter.prevent
@dragover.prevent
@dragstop="dropTask($event)"
>

=

<UCard class="mb-4"
@click="goToTask(task.id)"
draggable="true"
@dragstart="pickUpTask"
>
<strong>{{ task.name }}</strong>
<p>{{ task.description }}</p>
</UCard>

=

working code 

boardcolumn.vue

function pickupTask(event, { fromColumnIndex, fromTaskIndex }) {
event.dataTransfer.effectAllowed = 'move'
event.dataTransfer.dropEffect = 'move'
event.dataTransfer.setData('from-column-index', fromColumnIndex)
event.dataTransfer.setData('from-task-index', fromTaskIndex)
}

=

function dropTask(event, toColumnIndex) {
const fromColumnIndex = event.dataTransfer.getData('from-column-index')
const fromTaskIndex = event.dataTransfer.getData('from-task-index')

boardStore.moveTask({
taskIndex: fromTaskIndex,
fromColumnIndex,
toColumnIndex
})
}

=

<UContainer
class="column"
@dragenter.prevent
@dragover.prevent
@drop.stop="dropTask($event, columnIndex)"
>
<div class="column-header mb-4">
<div>
<UInput v-if="editNameState" type="text" v-model="column.name" />
<h2 v-else>{{ column.name }}</h2>
</div>
<div>
<UButton
icon="i-heroicons-pencil-square"
class="mr-2"
@click="editNameState = !editNameState"
/>
<UButton
icon="i-heroicons-trash"
color="red"
@click="deleteColumn(columnIndex)"
/>
</div>
</div>
<ul>
<li v-for="(task, taskIndex) in column.tasks" :key="task.id">
<UCard
class="mb-4"
@click="goToTask(task.id)"
draggable="true"
@dragstart="
pickupTask($event, {
fromColumnIndex: columnIndex,
fromTaskIndex: taskIndex
})
"
>
<strong>{{ task.name }}</strong>
<p>{{ task.description }}</p>
</UCard>
</li>
</ul>
<UInput
v-model="newTaskName"
type="text"
placeholder="Create new task"
icon="i-heroicons-plus-circle-solid"
@keyup.enter="addTask"
/>
</UContainer>

=

boardstore.js

function moveTask({ taskIndex, fromColumnIndex, toColumnIndex }) {
const task = board.value.columns[fromColumnIndex].tasks.splice(
taskIndex,
1
)[0]

board.value.columns[toColumnIndex].tasks.push(task)
}

=

boardstore.js

import { v4 as uuid } from 'uuid'
import { defineStore } from 'pinia'
import { useStorage } from '@vueuse/core'
import boardData from '~/data/board.json'

export const useBoardStore = defineStore('boardStore', () => {
const board = useStorage('board', boardData)

/**
* Tasks
*/
const getTask = computed(() => {
return taskId => {
for (const column of board.value.columns) {
const task = column.tasks.find(task => task.id === taskId)
if (task) return task
}
}
})

function moveTask({ taskIndex, fromColumnIndex, toColumnIndex }) {
const task = board.value.columns[fromColumnIndex].tasks.splice(
taskIndex,
1
)[0]

board.value.columns[toColumnIndex].tasks.push(task)
}

function addTask({ columnIndex, taskName }) {
board.value.columns[columnIndex].tasks.push({
id: uuid(),
name: taskName,
description: ''
})
}

function deleteTask(taskId) {
for (const column of board.value.columns) {
const taskIndex = column.tasks.findIndex(task => task.id === taskId)

if (taskIndex !== -1) {
column.tasks.splice(taskIndex, 1)
return
}
}
}

/**
* Columns
*/
function addColumn(columnName) {
board.value.columns.push({
name: columnName,
tasks: []
})
}

function deleteColumn(columnIndex) {
board.value.columns.splice(columnIndex, 1)
}

return {
/* State */
board,
/* Getters */
getTask,
moveTask,
/* Actions*/
addColumn,
addTask,
deleteColumn,
deleteTask
}
})

boardcolumn.vue

<script setup>
import { useBoardStore } from '../stores/boardStore'

const props = defineProps({
column: {
type: Object,
required: true
},
columnIndex: {
type: Number,
required: true
}
})

const boardStore = useBoardStore()
const router = useRouter()

const editNameState = ref(false)
const newTaskName = ref('')

function addTask() {
boardStore.addTask({
taskName: newTaskName.value,
columnIndex: props.columnIndex
})
newTaskName.value = ''
}

function deleteColumn(columnIndex) {
boardStore.deleteColumn(columnIndex)
}

function dropTask(event, toColumnIndex) {
const fromColumnIndex = event.dataTransfer.getData('from-column-index')
const fromTaskIndex = event.dataTransfer.getData('from-task-index')

boardStore.moveTask({
taskIndex: fromTaskIndex,
fromColumnIndex,
toColumnIndex
})
}

function goToTask(taskId) {
router.push(`/tasks/${taskId}`)
}

function pickupTask(event, { fromColumnIndex, fromTaskIndex }) {
event.dataTransfer.effectAllowed = 'move'
event.dataTransfer.dropEffect = 'move'
event.dataTransfer.setData('from-column-index', fromColumnIndex)
event.dataTransfer.setData('from-task-index', fromTaskIndex)
}
</script>

<template>
<UContainer
class="column"
@dragenter.prevent
@dragover.prevent
@drop.stop="dropTask($event, columnIndex)"
>
<div class="column-header mb-4">
<div>
<UInput v-if="editNameState" type="text" v-model="column.name" />
<h2 v-else>{{ column.name }}</h2>
</div>
<div>
<UButton
icon="i-heroicons-pencil-square"
class="mr-2"
@click="editNameState = !editNameState"
/>
<UButton
icon="i-heroicons-trash"
color="red"
@click="deleteColumn(columnIndex)"
/>
</div>
</div>
<ul>
<li v-for="(task, taskIndex) in column.tasks" :key="task.id">
<UCard
class="mb-4"
@click="goToTask(task.id)"
draggable="true"
@dragstart="
pickupTask($event, {
fromColumnIndex: columnIndex,
fromTaskIndex: taskIndex
})
"
>
<strong>{{ task.name }}</strong>
<p>{{ task.description }}</p>
</UCard>
</li>
</ul>
<UInput
v-model="newTaskName"
type="text"
placeholder="Create new task"
icon="i-heroicons-plus-circle-solid"
@keyup.enter="addTask"
/>
</UContainer>
</template>

=


==


dRAG And Drop COlumns

<UContainer
class="column"
draggable="true"
@dragstart.self="pickupColumn($event, columnIndex)"



function pickupColumn(event, fromColumnIndex) {
event.dataTransfer.effectAllowed = 'move'
event.dataTransfer.dropEffect = 'move'
event.dataTransfer.setData('type', 'column')
event.dataTransfer.setData('from-column-index', fromColumnIndex)
}

=

function dropItem(event, toColumnIndex) {
const type = event.dataTransfer.getData('type')
const fromColumnIndex = event.dataTransfer.getData('from-column-index')

if (type === 'task') {
const fromTaskIndex = event.dataTransfer.getData('from-task-index')

boardStore.moveTask({
taskIndex: fromTaskIndex,
fromColumnIndex,
toColumnIndex
})
} else if (type === 'column') {
boardStore.moveColumn({
fromColumnIndex,
toColumnIndex
})
}
}

=

move column in boardstore .js


function moveColumn({ fromColumnIndex, toColumnIndex }) {
const column = board.value.columns.splice(fromColumnIndex, 1)[0]
board.value.columns.splice(toColumnIndex, 0, column)
}


full code 

boardstore.js

import { v4 as uuid } from 'uuid'
import { defineStore } from 'pinia'
import { useStorage } from '@vueuse/core'
import boardData from '~/data/board.json'

export const useBoardStore = defineStore('boardStore', () => {
const board = useStorage('board', boardData)

/**
* Tasks
*/
const getTask = computed(() => {
return taskId => {
for (const column of board.value.columns) {
const task = column.tasks.find(task => task.id === taskId)
if (task) return task
}
}
})

function addTask({ columnIndex, taskName }) {
board.value.columns[columnIndex].tasks.push({
id: uuid(),
name: taskName,
description: ''
})
}

function deleteTask(taskId) {
for (const column of board.value.columns) {
const taskIndex = column.tasks.findIndex(task => task.id === taskId)

if (taskIndex !== -1) {
column.tasks.splice(taskIndex, 1)
return
}
}
}

function moveTask({ taskIndex, fromColumnIndex, toColumnIndex }) {
const task = board.value.columns[fromColumnIndex].tasks.splice(
taskIndex,
1
)[0]

board.value.columns[toColumnIndex].tasks.push(task)
}

/**
* Columns
*/
function addColumn(columnName) {
board.value.columns.push({
name: columnName,
tasks: []
})
}

function deleteColumn(columnIndex) {
board.value.columns.splice(columnIndex, 1)
}

function moveColumn({ fromColumnIndex, toColumnIndex }) {
const column = board.value.columns.splice(fromColumnIndex, 1)[0]
board.value.columns.splice(toColumnIndex, 0, column)
}

return {
/* State */
board,
/* Getters */
getTask,
/* Actions*/
addColumn,
addTask,
deleteColumn,
deleteTask,
moveColumn,
moveTask
}
})


boardcolumn.vue

<script setup>
import { useBoardStore } from '../stores/boardStore'

const props = defineProps({
column: {
type: Object,
required: true
},
columnIndex: {
type: Number,
required: true
}
})

const boardStore = useBoardStore()
const router = useRouter()

const editNameState = ref(false)
const newTaskName = ref('')

function addTask() {
boardStore.addTask({
taskName: newTaskName.value,
columnIndex: props.columnIndex
})
newTaskName.value = ''
}

function deleteColumn(columnIndex) {
boardStore.deleteColumn(columnIndex)
}

function dropItem(event, toColumnIndex) {
const type = event.dataTransfer.getData('type')
const fromColumnIndex = event.dataTransfer.getData('from-column-index')

if (type === 'task') {
const fromTaskIndex = event.dataTransfer.getData('from-task-index')

boardStore.moveTask({
taskIndex: fromTaskIndex,
fromColumnIndex,
toColumnIndex
})
} else if (type === 'column') {
boardStore.moveColumn({
fromColumnIndex,
toColumnIndex
})
}
}

function goToTask(taskId) {
router.push(`/tasks/${taskId}`)
}

function pickupColumn(event, fromColumnIndex) {
event.dataTransfer.effectAllowed = 'move'
event.dataTransfer.dropEffect = 'move'
event.dataTransfer.setData('type', 'column')
event.dataTransfer.setData('from-column-index', fromColumnIndex)
}

function pickupTask(event, { fromColumnIndex, fromTaskIndex }) {
event.dataTransfer.effectAllowed = 'move'
event.dataTransfer.dropEffect = 'move'
event.dataTransfer.setData('type', 'task')
event.dataTransfer.setData('from-column-index', fromColumnIndex)
event.dataTransfer.setData('from-task-index', fromTaskIndex)
}
</script>

<template>
<UContainer
class="column"
draggable="true"
@dragstart.self="pickupColumn($event, columnIndex)"
@dragenter.prevent
@dragover.prevent
@drop.stop="dropItem($event, columnIndex)"
>
<div class="column-header mb-4">
<div>
<UInput v-if="editNameState" type="text" v-model="column.name" />
<h2 v-else>{{ column.name }}</h2>
</div>
<div>
<UButton
icon="i-heroicons-pencil-square"
class="mr-2"
@click="editNameState = !editNameState"
/>
<UButton
icon="i-heroicons-trash"
color="red"
@click="deleteColumn(columnIndex)"
/>
</div>
</div>
<ul>
<li v-for="(task, taskIndex) in column.tasks" :key="task.id">
<UCard
class="mb-4"
@click="goToTask(task.id)"
draggable="true"
@dragstart="
pickupTask($event, {
fromColumnIndex: columnIndex,
fromTaskIndex: taskIndex
})
"
>
<strong>{{ task.name }}</strong>
<p>{{ task.description }}</p>
</UCard>
</li>
</ul>
<UInput
v-model="newTaskName"
type="text"
placeholder="Create new task"
icon="i-heroicons-plus-circle-solid"
@keyup.enter="addTask"
/>
</UContainer>
</template>


=

boardcolumn.vue

@drop.stop="dropItem($event, { toColumnIndex: columnIndex })"

<UContainer
class="column"
draggable="true"
@dragstart.self="pickupColumn($event, columnIndex)"
@dragenter.prevent
@dragover.prevent
@drop.stop="dropItem($event, { toColumnIndex: columnIndex })"
>

=

<UCard
class="mb-4"
@click="goToTask(task.id)"
draggable="true"
@dragstart="
pickupTask($event, {
fromColumnIndex: columnIndex,
fromTaskIndex: taskIndex
})
"
@drop.stop="
dropItem($event, {
toColumnIndex: columnIndex,
toTaskIndex: taskIndex
})
"


=


function moveTask({
fromTaskIndex,
toTaskIndex,
fromColumnIndex,
toColumnIndex
}) {
const task = board.value.columns[fromColumnIndex].tasks.splice(
fromTaskIndex,
1
)[0]

board.value.columns[toColumnIndex].tasks.splice(toTaskIndex, 0, task)
}


=

bonus with next steps

nuxt ui

uinotification (toast notifcication)

command panel

const toast = useToast()

=

function deleteTask() {
toast.add({
title: 'Task deleted',
description: `${task.value.name} has been deleted.`,
icon: 'i-heroicons-trash',
color: 'red'
})
boardStore.deleteTask(route.params.id)
router.push('/')
}

=


in app.vue


<script setup>
const route = useRoute();
console.log(route.name);
</script>
<template>
<Html class="bg-emerald-500">
<div>
<NuxtPage />
</div>
<UNotifications />
</Html>
</template>

=

delete [id].vue

const toast = useToast()


function deleteTask() {
toast.add({
title: 'Task deleted',
description: `${task.value.name} has been deleted.`,
icon: 'i-heroicons-trash',
color: 'red'
})
boardStore.deleteTask(route.params.id)
router.push('/')
}

=

 







No comments:

Post a Comment

React -1 ( react with ravenndra kanchi)

 react with tool chanis 45 to 50 sessions core  after 50 sessions -> some tooll chains == react with type script mern stackaplication in ...