Skip to content

Commit

Permalink
feat(node): enable node swapping
Browse files Browse the repository at this point in the history
  • Loading branch information
heristop committed Jul 1, 2024
1 parent 2db897f commit adfcc82
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 63 deletions.
20 changes: 11 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@

## ✨ Features

- **Migration Tracking**: Monitor the migration progress of your Information Systems or applications.
- **Task Management**: Track tasks, subtasks, and their statuses.
- **Bug Tracking**: Manage bug reports, assign them to developers, and monitor their resolution.
- **Production Management**: Visualize production stages, quality control statuses, and inventory levels.
- **Sales and Marketing**: Follow marketing campaigns, sales opportunities, and contracts.
- **HR Management**: Track recruitment processes, employee training programs, and more.
- **Customizable Statuses**: Define and customize statuses to fit your specific workflow.
- **Import/Export**: Import project data from JSON files and export current project data for backup or sharing.
- **And more...**
- **Migration Tracking**: Effortlessly monitor the progress of migrations and transitions to ensure smooth operations.
- **Task Management**: Efficiently track tasks, subtasks, and their statuses to keep your projects organized and on schedule.
- **Customizable Statuses**: Define and customize statuses to fit your specific workflow, providing flexibility for various project needs.
- **Import/Export**: Easily import project data from JSON files and export current project data for backup or sharing.
- **Responsive Design**: Enjoy a seamless experience across desktops, tablets, and mobile devices with a fully responsive interface.
- **Editing and Customization**: Edit node labels, add new nodes, and delete or move existing nodes to tailor your project structure.
- **Drag and Drop**: Intuitively drag and drop nodes within the same level to reorder tasks and sections.
- **Visual Indicators**: Use visual cues to represent statuses, progress, and other key project metrics.
- **Real-Time Updates**: Receive instant updates as changes are made, ensuring all team members are on the same page.
- **User-Friendly Interface**: Navigate through a clean, intuitive interface designed for ease of use and productivity.
- **And More**: Discover additional features designed to optimize your project management experience.

## 🛠️ Installation

Expand Down
21 changes: 21 additions & 0 deletions app/components/TreeNode.vue
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,23 @@ const deleteNode = (event: MouseEvent) => {
store.deleteSection(props.node.key)
}
const handleDragStart = (event: DragEvent) => {
event.stopPropagation()
event.dataTransfer?.setData('text/plain', props.node.key)
}
const handleDrop = (event: DragEvent) => {
event.stopPropagation()
const draggedKey = event.dataTransfer?.getData('text/plain')
if (draggedKey && draggedKey !== props.node.key) {
store.swapSections(props.node.key, draggedKey)
}
}
const handleDragOver = (event: DragEvent) => {
event.preventDefault()
}
watchEffect(() => {
nodeStatus.value = props.node.status || store.statuses[0]?.name || ''
checkIfSuccessNode(props.node)
Expand Down Expand Up @@ -225,7 +242,11 @@ const applySuccessAnimation = (node: Section) => {
:class="{ 'success-animation': isSuccessNode }"
:style="nodeStyle"
:data-node-key="node.key"
draggable="true"
@click="handleClick"
@dragstart="handleDragStart"
@drop="handleDrop"
@dragover="handleDragOver"
>
<div :class="['node-title', { 'center-title': !node.children || !node.children.length }]">
<span
Expand Down
137 changes: 86 additions & 51 deletions app/composables/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export const useStore = defineStore('store', {
},
applyDefaultStatus(sections: Section[], existingProjects: Section[]): Section[] {
return sections.map((node) => {
const existingProject = this.findProjectByKey(node.key, existingProjects)
const existingProject = this.findSectionByKey(node.key, existingProjects)

if (!node.status) {
if (existingProject) {
Expand Down Expand Up @@ -135,60 +135,85 @@ export const useStore = defineStore('store', {
}))
},
updateSectionStatus(key: string, status: string) {
const section = this.findProjectByKey(key, this.sections)
const section = this.findSectionByKey(key, this.sections)

if (section) {
section.status = status
this.updateParentStatuses()
}
},
updateSectionCollapse(key: string) {
const section = this.findProjectByKey(key, this.sections)
const section = this.findSectionByKey(key, this.sections)

if (section) {
section.isCollapsed = !section.isCollapsed
}
},
addSection(parentKey: string, newSection: Section) {
const parent = this.findProjectByKey(parentKey, this.sections)
if (parent) {
if (!parent.children) {
parent.children = []
}
parent.children.push(newSection)
updateSectionKey(key: string, newKey: string) {
const section = this.findSectionByKey(key, this.sections)
if (section) {
section.key = newKey
}
},
addSiblingSection(parentKey: string, newSection: Section) {
const parent = this.findParentByKey(parentKey, this.sections)

if (parent) {
parent.children.push(newSection)

return
updateSectionName(key: string, newName: string) {
const section = this.findSectionByKey(key, this.sections)
if (section) {
section.name = newName
}
},
addSection(key: string, newSection: Section) {
const parentSection = this.findSectionByKey(key, this.sections)
if (parentSection) {
parentSection.children.push(newSection)
}
},
addSiblingSection(key: string, newSection: Section) {
const parentSection = this.findParentSectionByKey(key, this.sections)
if (parentSection) {
parentSection.children.push(newSection)
}
else {
this.sections.push(newSection)
}

this.sections.push(newSection)
},
deleteSection(key: string) {
const deleteRecursively = (sections: Section[], key: string): Section[] => {
return sections
.filter(section => section.key !== key)
.map(section => ({
...section,
children: section.children ? deleteRecursively(section.children, key) : [],
}))
const parentSection = this.findParentSectionByKey(key, this.sections)
if (parentSection) {
parentSection.children = parentSection.children.filter(child => child.key !== key)
}
else {
this.sections = this.sections.filter(section => section.key !== key)
}
},
swapSections(key1: string, key2: string) {
const parentSection1 = this.findParentSectionByKey(key1, this.sections)
const parentSection2 = this.findParentSectionByKey(key2, this.sections)

if (parentSection1 && parentSection2) {
const index1 = parentSection1.children.findIndex(child => child.key === key1)
const index2 = parentSection2.children.findIndex(child => child.key === key2)

if (index1 !== -1 && index2 !== -1) {
[parentSection1.children[index1], parentSection2.children[index2]] = [parentSection2.children[index2], parentSection1.children[index1]]
}
}
else {
const index1 = this.sections.findIndex(section => section.key === key1)
const index2 = this.sections.findIndex(section => section.key === key2)

this.sections = deleteRecursively(this.sections, key)
if (index1 !== -1 && index2 !== -1) {
[this.sections[index1], this.sections[index2]] = [this.sections[index2], this.sections[index1]]
}
}
},
findProjectByKey(key: string, sections: Section[]): Section | undefined {
findSectionByKey(key: string, sections: Section[]): Section | undefined {
for (const node of sections) {
if (node.key === key) {
return node
}

if (node.children) {
const found = this.findProjectByKey(key, node.children)
const found = this.findSectionByKey(key, node.children)

if (found) {
return found
Expand All @@ -198,18 +223,16 @@ export const useStore = defineStore('store', {

return undefined
},
findParentByKey(key: string, sections: Section[]): Section | undefined {
findParentSectionByKey(key: string, sections: Section[]): Section | undefined {
for (const node of sections) {
if (node.children) {
for (const child of node.children) {
if (child.key === key) {
return node
}
}
const parent = this.findParentByKey(key, node.children)
if (node.children && node.children.some(child => child.key === key)) {
return node
}

if (parent) {
return parent
if (node.children) {
const found = this.findParentSectionByKey(key, node.children)
if (found) {
return found
}
}
}
Expand Down Expand Up @@ -254,18 +277,6 @@ export const useStore = defineStore('store', {
removeStatus(index: number) {
this.statuses.splice(index, 1)
},
updateSectionLabel(key: string, newLabel: string) {
const section = this.findProjectByKey(key, this.sections)
if (section) {
if (this.displayLabel === 'key') {
section.key = newLabel

return
}

section.name = newLabel
}
},
toggleEditingMode() {
this.isEditingMode = !this.isEditingMode
},
Expand All @@ -275,4 +286,28 @@ export const useStore = defineStore('store', {
this.configLoaded = false
},
},
getters: {
duplicateProjects(state): { sections: string[], keys: string[] } {
const sections: string[] = []
const keys: string[] = []

const checkDuplicates = (nodes: Section[]) => {
nodes.forEach((node) => {
sections.push(node.name)
keys.push(node.key ?? '')

if (node.children) {
checkDuplicates(node.children)
}
})
}

checkDuplicates(state.sections)

return {
sections: sections.filter((item, index) => sections.indexOf(item) !== index),
keys: keys.filter((item, index) => keys.indexOf(item) !== index),
}
},
},
})
3 changes: 2 additions & 1 deletion app/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
"key": { "type": "string" },
"name": { "type": "string" },
"status": { "type": "string", "nullable": true },
"children": { "$ref": "#" }
"children": { "$ref": "#" },
"isCollapsed": { "type": "boolean", "nullable": true }
},
"required": ["key", "name"],
"additionalProperties": false
Expand Down
Loading

0 comments on commit adfcc82

Please sign in to comment.