fix: adjust component sizes on demo page

This commit is contained in:
2025-08-30 16:44:46 +03:30
parent e5c8465287
commit 7325e18cec
5 changed files with 637 additions and 592 deletions

18
package-lock.json generated
View File

@@ -39,6 +39,8 @@
"eslint-plugin-regexp": "2.7.0",
"file-saver": "^2.0.5",
"gridstack": "^12.2.1",
"highcharts": "^12.3.0",
"highcharts-vue": "^2.0.1",
"jspdf": "^3.0.1",
"jspdf-autotable": "^5.0.2",
"jwt-decode": "4.0.0",
@@ -9065,6 +9067,22 @@
"dev": true,
"license": "MIT"
},
"node_modules/highcharts": {
"version": "12.3.0",
"resolved": "https://registry.npmjs.org/highcharts/-/highcharts-12.3.0.tgz",
"integrity": "sha512-QIKmaemgheRa1K2Ia9MLj1KLtBU1Tu/VQ6KAMqtMBMsAC4NzcFq6g96LF03ZO3IFFiSifmZx8ItEyRcz4w75cg==",
"license": "https://www.highcharts.com/license"
},
"node_modules/highcharts-vue": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/highcharts-vue/-/highcharts-vue-2.0.1.tgz",
"integrity": "sha512-7yVaKvsWlx7OgmKFXE2D99fi/0tr2A85H4KUz3jL7CRQhAwvEvXgtDIyTBGLHJsOC5L9VlppAI7k6KfIi0j0hg==",
"license": "SEE LICENSE IN LICENSE",
"peerDependencies": {
"highcharts": ">=5.0.0",
"vue": ">=3.0.0"
}
},
"node_modules/hookable": {
"version": "5.5.3",
"resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz",

View File

@@ -42,6 +42,8 @@
"eslint-plugin-regexp": "2.7.0",
"file-saver": "^2.0.5",
"gridstack": "^12.2.1",
"highcharts": "^12.3.0",
"highcharts-vue": "^2.0.1",
"jspdf": "^3.0.1",
"jspdf-autotable": "^5.0.2",
"jwt-decode": "4.0.0",

View File

@@ -102,7 +102,6 @@ const cardOrder = ref([
"leads2",
"leads3",
"project-activity",
"gantt-chart", // اضافه کردن gantt chart
"analysis1",
"analysis2",
"cost-overview",
@@ -120,7 +119,6 @@ const defaultWidgetIds = [
"leads2",
"leads3",
"project-activity",
"gantt-chart", // اضافه کردن
"analysis1",
"analysis2",
"cost-overview",
@@ -147,7 +145,6 @@ const cardSizes = ref({
"project-status": { cols: 4, height: 33.33 },
"active-project": { cols: 4, height: 33.33 },
"recent-transactions": { cols: 6, height: 33.33 },
"gantt-chart": { cols: 12, height: 50 },
"activity-timeline": { cols: 6, height: 33.33 },
"ecommerce-congratulations": { cols: 6, height: 33.33 },
"ecommerce-earning-reports": { cols: 8, height: 33.33 },
@@ -186,7 +183,6 @@ const cardComponents = {
"project-status": { component: CrmProjectStatus, props: {} },
"active-project": { component: CrmActiveProject, props: {} },
"recent-transactions": { component: CrmRecentTransactions, props: {} },
"gantt-chart": { component: GanttChart, props: {} },
"activity-timeline": { component: CrmActivityTimeline, props: {} },
"ecommerce-congratulations": { component: EcommerceCongratulationsJohn, props: {} },
"ecommerce-earning-reports": { component: EcommerceEarningReports, props: {} },

View File

@@ -2,96 +2,78 @@
import { onMounted, nextTick } from 'vue'
import { GridStack } from 'gridstack'
import EcommerceCongratulationsJohn from '@/views/dashboards/ecommerce/EcommerceCongratulationsJohn.vue'
import EcommerceEarningReports from '@/views/dashboards/ecommerce/EcommerceEarningReports.vue'
import EcommerceExpensesRadialBarCharts from '@/views/dashboards/ecommerce/EcommerceExpensesRadialBarCharts.vue'
import EcommerceGeneratedLeads from '@/views/dashboards/ecommerce/EcommerceGeneratedLeads.vue'
import EcommerceInvoiceTable from '@/views/dashboards/ecommerce/EcommerceInvoiceTable.vue'
import EcommerceOrder from '@/views/dashboards/ecommerce/EcommerceOrder.vue'
import EcommercePopularProducts from '@/views/dashboards/ecommerce/EcommercePopularProducts.vue'
import EcommerceRevenueReport from '@/views/dashboards/ecommerce/EcommerceRevenueReport.vue'
import EcommerceStatistics from '@/views/dashboards/ecommerce/EcommerceStatistics.vue'
import EcommerceTotalProfitLineCharts from '@/views/dashboards/ecommerce/EcommerceTotalProfitLineCharts.vue'
import EcommerceTransactions from '@/views/dashboards/ecommerce/EcommerceTransactions.vue'
import ProjectActivityBarChart from "@/components/ProjectActivityBarChart.vue";
import AnalysisCard from "@/components/AnalysisCard.vue";
import CostOverview from "@/components/CostOverview.vue";
import GeneratedLeadsCard from "@/views/dashboards/ecommerce/EcommerceGeneratedLeads.vue";
import GanttChart from './gantt.vue'
onMounted(async () => {
const grid = GridStack.init({
column: 12,
cellHeight: 50,
cellHeight: '110',
float: true,
draggable: { handle: '.grid-stack-item-content' },
resizable: true
resizable: true,
maxRow: 20,
})
await nextTick()
})
</script>
<template>
<div class="grid-stack">
<div class="grid-stack-item" gs-w="8" gs-h="4" gs-max-h="4" gs-x="0" gs-y="0">
<div class="grid-stack-item" gs-min-w="4" gs-h="2" gs-max-h="2">
<div class="grid-stack-item-content">
<EcommerceCongratulationsJohn />
<GeneratedLeadsCard :progress="32" />
</div>
</div>
<div class="grid-stack-item" gs-w="4" gs-h="4" gs-max-h="4" gs-x="8" gs-y="0">
<div class="grid-stack-item" gs-min-w="4" gs-h="2" gs-max-h="2">
<div class="grid-stack-item-content">
<EcommerceStatistics />
<GeneratedLeadsCard :donut-colors="['primary']" :progress="71" />
</div>
</div>
<div class="grid-stack-item" gs-w="7" gs-h="3" gs-max-h="3" gs-x="0" gs-y="4">
<div class="grid-stack-item" gs-min-w="4" gs-h="2" gs-max-h="2">
<div class="grid-stack-item-content">
<EcommerceTotalProfitLineCharts />
<GeneratedLeadsCard :donut-colors="['warning']" :progress="32" />
</div>
</div>
<div class="grid-stack-item" gs-w="5" gs-h="3" gs-max-h="3" gs-x="7" gs-y="4">
<div class="grid-stack-item" gs-w="8" gs-h="7" gs-max-h="7">
<div class="grid-stack-item-content">
<EcommerceExpensesRadialBarCharts />
<ProjectActivityBarChart />
</div>
</div>
<div class="grid-stack-item" gs-w="3" gs-h="5" gs-max-h="5" gs-x="0" gs-y="7">
<div class="grid-stack-item" gs-w="4" gs-h="7" gs-max-h="7">
<div class="grid-stack-item-content">
<EcommerceGeneratedLeads />
<AnalysisCard />
</div>
</div>
<div class="grid-stack-item" gs-w="6" gs-h="5" gs-max-h="5" gs-x="3" gs-y="7">
<div class="grid-stack-item" gs-w="12" gs-h="8" gs-max-h="8">
<div class="grid-stack-item-content">
<EcommerceRevenueReport />
</div>
</div>
<div class="grid-stack-item" gs-w="3" gs-h="3" gs-max-h="3" gs-x="9" gs-y="7">
<div class="grid-stack-item-content">
<EcommerceOrder />
</div>
</div>
<div class="grid-stack-item" gs-w="4" gs-h="10" gs-max-h="10" gs-x="0" gs-y="12">
<div class="grid-stack-item-content">
<EcommerceEarningReports />
</div>
</div>
<div class="grid-stack-item" gs-w="4" gs-h="10" gs-max-h="10" gs-x="4" gs-y="12">
<div class="grid-stack-item-content">
<EcommercePopularProducts />
</div>
</div>
<div class="grid-stack-item" gs-w="4" gs-h="4" gs-max-h="4" gs-x="8" gs-y="12">
<div class="grid-stack-item-content">
<EcommerceTransactions />
</div>
</div>
<div class="grid-stack-item" gs-w="12" gs-h="6" gs-max-h="6" gs-x="0" gs-y="16">
<div class="grid-stack-item-content">
<EcommerceInvoiceTable />
<GanttChart />
</div>
</div>
</div>
</template>
<style>
.grid-stack-item-content {
height: 100%;
display: flex;
flex-direction: column;
}
.grid-stack-item-content>* {
flex: 1;
margin-bottom: 3.2rem;
}
.grid-stack-item:has(GanttChart) {
height: 100vh !important;
}
.grid-stack-item:has(GanttChart) .grid-stack-item-content {
height: 100vh;
}
</style>

View File

@@ -1,23 +1,36 @@
<script>
<script setup>
import { ref, onMounted, onBeforeUnmount } from 'vue'
import { gantt } from 'dhtmlx-gantt'
import 'dhtmlx-gantt/codebase/dhtmlxgantt.css'
export default {
name: 'GanttChart',
data() {
return {
showDeleteModal: false,
showEditModal: false,
showAddModal: false,
currentTaskId: null,
currentTask: null,
taskForm: {
const props = defineProps({
initialTasks: {
type: Object,
default: () => ({
data: [],
links: []
})
},
config: {
type: Object,
default: () => ({})
}
})
const ganttContainer = ref(null)
const showDeleteModal = ref(false)
const showEditModal = ref(false)
const showAddModal = ref(false)
const currentTaskId = ref(null)
const currentTask = ref(null)
const taskForm = ref({
text: '',
start_date: '',
duration: 3,
progress: 0
},
tasks: {
})
const tasks = props.initialTasks.data.length > 0 ? props.initialTasks : {
data: [
{
id: 1,
@@ -205,18 +218,8 @@ export default {
{ id: 11, source: 241, target: 242, type: "0" }
]
}
}
},
mounted() {
this.initGantt()
},
beforeUnmount() {
if (gantt.$container) {
gantt.clearAll()
}
},
methods: {
initGantt() {
const initGantt = () => {
gantt.config.date_format = "%d-%m-%Y"
gantt.config.fit_tasks = true
gantt.config.start_date = null
@@ -310,17 +313,18 @@ export default {
gantt.attachEvent("onContextMenu", function(taskId, linkId, e) {
if (taskId) {
this.showContextMenu(e, taskId)
showContextMenu(e, taskId)
return false
}
return true
}.bind(this))
})
gantt.init(this.$refs.ganttContainer)
gantt.parse(this.tasks)
},
showContextMenu(e, taskId) {
this.hideContextMenu()
gantt.init(ganttContainer.value)
gantt.parse(tasks)
}
const showContextMenu = (e, taskId) => {
hideContextMenu()
const contextMenu = document.createElement('div')
contextMenu.id = 'gantt-context-menu'
@@ -364,139 +368,156 @@ export default {
document.body.appendChild(contextMenu)
document.addEventListener('click', this.hideContextMenu)
window.ganttComponent = this
},
hideContextMenu() {
document.addEventListener('click', hideContextMenu)
window.ganttComponent = {
addSubTask,
editTask,
deleteTaskFromContext,
markTaskComplete
}
}
const hideContextMenu = () => {
const existingMenu = document.getElementById('gantt-context-menu')
if (existingMenu) {
existingMenu.remove()
}
document.removeEventListener('click', this.hideContextMenu)
},
addSubTask(parentId) {
this.currentTaskId = parentId
this.taskForm = {
document.removeEventListener('click', hideContextMenu)
}
const addSubTask = (parentId) => {
currentTaskId.value = parentId
taskForm.value = {
text: 'New Sub Task',
start_date: new Date().toISOString().split('T')[0],
duration: 3,
progress: 0
}
this.showAddModal = true
this.hideContextMenu()
},
editTask(taskId) {
this.currentTaskId = taskId
showAddModal.value = true
hideContextMenu()
}
const editTask = (taskId) => {
currentTaskId.value = taskId
const task = gantt.getTask(taskId)
this.currentTask = task
this.taskForm = {
currentTask.value = task
taskForm.value = {
text: task.text,
start_date: gantt.date.date_to_str("%Y-%m-%d")(task.start_date),
duration: task.duration,
progress: task.progress
}
this.showEditModal = true
this.hideContextMenu()
},
deleteTaskFromContext(taskId) {
this.currentTaskId = taskId
this.currentTask = gantt.getTask(taskId)
this.showDeleteModal = true
this.hideContextMenu()
},
markTaskComplete(taskId) {
showEditModal.value = true
hideContextMenu()
}
const deleteTaskFromContext = (taskId) => {
currentTaskId.value = taskId
currentTask.value = gantt.getTask(taskId)
showDeleteModal.value = true
hideContextMenu()
}
const markTaskComplete = (taskId) => {
let task = gantt.getTask(taskId)
task.progress = task.progress === 1 ? 0 : 1
gantt.updateTask(taskId)
gantt.render()
this.hideContextMenu()
},
addTask() {
this.currentTaskId = null
this.taskForm = {
hideContextMenu()
}
const addTask = () => {
currentTaskId.value = null
taskForm.value = {
text: 'New Task',
start_date: new Date().toISOString().split('T')[0],
duration: 3,
progress: 0
}
this.showAddModal = true
},
deleteTask() {
showAddModal.value = true
}
const deleteTask = () => {
const selectedTask = gantt.getSelectedId()
if (selectedTask) {
this.currentTaskId = selectedTask
this.currentTask = gantt.getTask(selectedTask)
this.showDeleteModal = true
currentTaskId.value = selectedTask
currentTask.value = gantt.getTask(selectedTask)
showDeleteModal.value = true
} else {
this.showNotification('Please select a task to delete', 'warning')
showNotification('Please select a task to delete', 'warning')
}
},
confirmDelete() {
if (this.currentTaskId) {
gantt.deleteTask(this.currentTaskId)
this.showDeleteModal = false
this.currentTaskId = null
this.currentTask = null
this.showNotification('Task deleted successfully', 'success')
}
},
saveTask() {
if (!this.taskForm.text.trim()) {
this.showNotification('Task name is required', 'error')
const confirmDelete = () => {
if (currentTaskId.value) {
gantt.deleteTask(currentTaskId.value)
showDeleteModal.value = false
currentTaskId.value = null
currentTask.value = null
showNotification('Task deleted successfully', 'success')
}
}
const saveTask = () => {
if (!taskForm.value.text.trim()) {
showNotification('Task name is required', 'error')
return
}
const taskData = {
text: this.taskForm.text,
start_date: gantt.date.str_to_date("%Y-%m-%d")(this.taskForm.start_date),
duration: parseInt(this.taskForm.duration),
progress: parseFloat(this.taskForm.progress)
text: taskForm.value.text,
start_date: gantt.date.str_to_date("%Y-%m-%d")(taskForm.value.start_date),
duration: parseInt(taskForm.value.duration),
progress: parseFloat(taskForm.value.progress)
}
if (this.showEditModal) {
Object.assign(this.currentTask, taskData)
gantt.updateTask(this.currentTaskId)
if (showEditModal.value) {
Object.assign(currentTask.value, taskData)
gantt.updateTask(currentTaskId.value)
gantt.render()
this.showEditModal = false
this.showNotification('Task updated successfully', 'success')
} else if (this.showAddModal) {
showEditModal.value = false
showNotification('Task updated successfully', 'success')
} else if (showAddModal.value) {
const newTaskId = gantt.uid()
const newTask = {
id: newTaskId,
...taskData
}
if (this.currentTaskId) {
newTask.parent = this.currentTaskId
if (currentTaskId.value) {
newTask.parent = currentTaskId.value
}
gantt.addTask(newTask)
gantt.refreshData()
gantt.selectTask(newTaskId)
gantt.showTask(newTaskId)
this.showAddModal = false
this.showNotification('Task added successfully', 'success')
showAddModal.value = false
showNotification('Task added successfully', 'success')
}
this.resetForm()
},
resetForm() {
this.currentTaskId = null
this.currentTask = null
this.taskForm = {
resetForm()
}
const resetForm = () => {
currentTaskId.value = null
currentTask.value = null
taskForm.value = {
text: '',
start_date: '',
duration: 3,
progress: 0
}
},
closeModal() {
this.showDeleteModal = false
this.showEditModal = false
this.showAddModal = false
this.resetForm()
},
showNotification(message, type = 'info') {
}
const closeModal = () => {
showDeleteModal.value = false
showEditModal.value = false
showAddModal.value = false
resetForm()
}
const showNotification = (message, type = 'info') => {
const notification = document.createElement('div')
notification.className = `notification notification-${type}`
notification.innerHTML = `
@@ -512,28 +533,53 @@ export default {
notification.remove()
}
}, 4000)
},
markCompleted() {
}
const markCompleted = () => {
const selectedTask = gantt.getSelectedId()
if (selectedTask) {
let task = gantt.getTask(selectedTask)
task.progress = task.progress === 1 ? 0 : 1
gantt.updateTask(selectedTask)
gantt.render()
this.showNotification(
showNotification(
`Task marked as ${task.progress === 1 ? 'completed' : 'incomplete'}`,
'success'
)
} else {
this.showNotification('Please select a task to mark as completed', 'warning')
showNotification('Please select a task to mark as completed', 'warning')
}
}
onMounted(() => {
initGantt()
// Prevent GridStack drag when interacting with Gantt chart
const ganttElement = ganttContainer.value
if (ganttElement) {
ganttElement.addEventListener('mousedown', (e) => {
e.stopPropagation()
})
ganttElement.addEventListener('dragstart', (e) => {
e.stopPropagation()
})
ganttElement.addEventListener('drag', (e) => {
e.stopPropagation()
})
}
})
onBeforeUnmount(() => {
if (gantt.$container) {
gantt.clearAll()
}
})
</script>
<template>
<div class="gantt-container">
<div class="gantt-container no-grid-drag">
<div class="gantt-toolbar">
<button @click="addTask" class="btn btn-primary">
<svg class="btn-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
@@ -557,9 +603,8 @@ export default {
Toggle Complete
</button>
</div>
<div ref="ganttContainer" class="gantt-chart"></div>
<div ref="ganttContainer" class="gantt-chart no-grid-drag"></div>
<!-- Delete Modal -->
<div v-if="showDeleteModal" class="modal-overlay" @click="closeModal">
<div class="modal-container" @click.stop>
<div class="modal-header">
@@ -586,7 +631,6 @@ export default {
</div>
</div>
<!-- Edit Modal -->
<div v-if="showEditModal" class="modal-overlay" @click="closeModal">
<div class="modal-container" @click.stop>
<div class="modal-header">
@@ -659,7 +703,6 @@ export default {
</div>
</div>
<!-- Add Modal -->
<div v-if="showAddModal" class="modal-overlay" @click="closeModal">
<div class="modal-container" @click.stop>
<div class="modal-header">
@@ -965,6 +1008,7 @@ export default {
opacity: 0;
transform: scale(0.95);
}
to {
opacity: 1;
transform: scale(1);
@@ -1024,6 +1068,7 @@ export default {
from {
opacity: 0;
}
to {
opacity: 1;
}
@@ -1046,6 +1091,7 @@ export default {
opacity: 0;
transform: translateY(-16px) scale(0.98);
}
to {
opacity: 1;
transform: translateY(0) scale(1);
@@ -1234,6 +1280,7 @@ export default {
opacity: 0;
transform: translateX(100%);
}
to {
opacity: 1;
transform: translateX(0);