Commit ed89bf27 by sawit

แก้ไข widget

parent aac8ace4
...@@ -135,7 +135,7 @@ ...@@ -135,7 +135,7 @@
border-radius: 0.75rem; border-radius: 0.75rem;
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
border: 1px solid #e5e7eb; border: 1px solid #e5e7eb;
min-height: 600px; min-height: 1200px;
} }
/* Empty State */ /* Empty State */
...@@ -144,7 +144,7 @@ ...@@ -144,7 +144,7 @@
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
height: 600px; height: 1200px;
text-align: center; text-align: center;
color: #6b7280; color: #6b7280;
} }
...@@ -437,39 +437,39 @@ ...@@ -437,39 +437,39 @@
/* Ensure widgets don't exceed panel boundaries and fill full space */ /* Ensure widgets don't exceed panel boundaries and fill full space */
.e-panel .e-panel-container .content > * { .e-panel .e-panel-container .content > * {
max-width: 100% !important;
max-height: 100% !important;
width: 100% !important; width: 100% !important;
height: 100% !important; height: 100% !important;
overflow: hidden !important; max-width: 100% !important;
box-sizing: border-box !important; min-width: 0 !important;
flex: 1 !important;
display: flex !important;
flex-direction: column !important;
} }
/* Specific widget component styling */ /* Specific styling for Syncfusion Datagrid Widget */
.e-panel .e-panel-container .content > * { .e-panel .e-panel-container .content app-syncfusion-datagrid-widget {
width: 100% !important; width: 100% !important;
height: 100% !important; height: 100% !important;
flex: 1 !important;
display: flex !important; display: flex !important;
flex-direction: column !important; flex-direction: column !important;
min-height: 0 !important; margin: 0 !important;
} padding: 0 !important;
/* Ensure all child elements fill the space */
.e-panel .e-panel-container .content > * > * {
width: 100% !important;
height: 100% !important;
flex: 1 !important;
min-height: 0 !important;
} }
/* Force all nested elements to use full space */ /* Force all nested elements to use full space */
.e-panel .e-panel-container .content * { .e-panel .e-panel-container .content * {
box-sizing: border-box !important; box-sizing: border-box !important;
} }
/* Remove margin/padding from grid container and grid */
.e-panel .e-panel-container .content app-syncfusion-datagrid-widget .grid-container {
margin: 0 !important;
padding: 0 !important;
}
.e-panel .e-panel-container .content app-syncfusion-datagrid-widget .grid-container .ejs-grid {
margin: 0 !important;
padding: 0 !important;
width: 100% !important;
max-width: 100% !important;
}
} }
} }
......
...@@ -486,6 +486,12 @@ export class DashboardManagementComponent implements OnInit, OnDestroy { ...@@ -486,6 +486,12 @@ export class DashboardManagementComponent implements OnInit, OnDestroy {
originalWidget: widget originalWidget: widget
}; };
// Force SyncfusionDatagridWidgetComponent to use full width
if (widget.component === 'SyncfusionDatagridWidgetComponent') {
panel.sizeX = this.columns;
panel.maxSizeX = this.columns;
}
return panel; return panel;
}) })
......
/* Dashboard Viewer Styles - Clean and Responsive */ // /* Dashboard Viewer Styles - Clean and Responsive */
/* Background */ // /* Background */
// .dashboard-background { // // .dashboard-background {
// background: linear-gradient(to bottom right, #f8fafc, #e0e7ff); // // background: linear-gradient(to bottom right, #f8fafc, #e0e7ff);
// // min-height: 100vh;
// // }
// /* Error Message */
// .error-message {
// display: flex;
// justify-content: center;
// align-items: center;
// min-height: 100vh; // min-height: 100vh;
// background: linear-gradient(to bottom right, #f8fafc, #e0e7ff);
// } // }
/* Error Message */ // .error-message-content {
.error-message { // background: white;
display: flex; // padding: 2rem;
justify-content: center; // border-radius: 12px;
align-items: center; // box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
min-height: 100vh; // border: 1px solid #e5e7eb;
background: linear-gradient(to bottom right, #f8fafc, #e0e7ff); // text-align: center;
} // max-width: 500px;
// }
.error-message-content {
background: white;
padding: 2rem;
border-radius: 12px;
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
border: 1px solid #e5e7eb;
text-align: center;
max-width: 500px;
}
.error-icon { // .error-icon {
font-size: 3rem; // font-size: 3rem;
color: #ef4444; // color: #ef4444;
margin-bottom: 1rem; // margin-bottom: 1rem;
} // }
.error-text { // .error-text {
font-size: 1.25rem; // font-size: 1.25rem;
font-weight: 600; // font-weight: 600;
color: #374151; // color: #374151;
display: block; // display: block;
margin-bottom: 0.5rem; // margin-bottom: 0.5rem;
} // }
.error-description { // .error-description {
color: #6b7280; // color: #6b7280;
font-size: 1rem; // font-size: 1rem;
} // }
/* Layout Classes */ // /* Layout Classes */
.action-buttons { // .action-buttons {
display: flex; // display: flex;
align-items: center; // align-items: center;
gap: 0.75rem; // gap: 0.75rem;
flex-shrink: 0; // flex-shrink: 0;
flex-wrap: wrap; // flex-wrap: wrap;
} // }
.view-dashboard-section { // .view-dashboard-section {
margin-bottom: 1rem; // margin-bottom: 1rem;
} // }
.dashboard-header-container { .dashboard-header-container {
background: white; background: white;
...@@ -126,34 +126,34 @@ ...@@ -126,34 +126,34 @@
margin-top: 0.25rem; margin-top: 0.25rem;
} }
.dashboard-selector { // .dashboard-selector {
display: flex; // display: flex;
align-items: center; // align-items: center;
gap: 0.75rem; // gap: 0.75rem;
flex-shrink: 0; // flex-shrink: 0;
} // }
.selector-label {
font-size: 0.875rem;
font-weight: 500;
color: #374151;
}
.dashboard-select { // .selector-label {
padding: 0.5rem 1rem; // font-size: 0.875rem;
border: 1px solid #d1d5db; // font-weight: 500;
border-radius: 0.5rem; // color: #374151;
background: white; // }
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
min-width: 200px;
font-size: 0.875rem;
&:focus { // .dashboard-select {
outline: none; // padding: 0.5rem 1rem;
border-color: #4f46e5; // border: 1px solid #d1d5db;
box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.1); // border-radius: 0.5rem;
} // background: white;
} // box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
// min-width: 200px;
// font-size: 0.875rem;
// &:focus {
// outline: none;
// border-color: #4f46e5;
// box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.1);
// }
// }
.dataset-picker-section { .dataset-picker-section {
display: flex; display: flex;
...@@ -162,541 +162,541 @@ ...@@ -162,541 +162,541 @@
flex-shrink: 0; flex-shrink: 0;
} }
/* Dashboard Content Area - Full Width and Responsive */ // /* Dashboard Content Area - Full Width and Responsive */
.dashboard-content-area { // .dashboard-content-area {
width: 100%;
min-height: 100vh;
padding: 0;
margin: 0;
background: linear-gradient(to bottom right, #f8fafc, #e0e7ff);
}
/* Dashboard Layout Container - Clean and Responsive */
// .dashboard-layout-container {
// width: 100%; // width: 100%;
// min-height: 100vh; // min-height: 100vh;
// padding: 1rem; // padding: 0;
// box-sizing: border-box; // margin: 0;
// } // background: linear-gradient(to bottom right, #f8fafc, #e0e7ff);
// }
/* Dashboard Layout - Responsive Grid */
.dashboard-layout {
width: 100% !important;
height: auto !important;
min-height: calc(100vh - 2rem);
background: transparent;
.e-panel {
border-radius: 12px;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
border: 1px solid #e5e7eb;
background: white;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
margin: 0;
&:hover {
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
transform: translateY(-2px);
}
}
.e-panel-header {
display: none !important; /* Hide panel headers in viewer mode */
}
.e-panel-content {
border-radius: 12px;
overflow: hidden;
position: relative;
width: 100%;
height: 100%;
padding: 0;
margin: 0;
}
}
.dashboard-content-card {
background: white;
border-radius: 0.75rem;
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
border: 1px solid #e5e7eb;
min-height: 600px;
}
/* Empty State */ // /* Dashboard Layout Container - Clean and Responsive */
.empty-state { // // .dashboard-layout-container {
display: flex; // // width: 100%;
flex-direction: column; // // min-height: 100vh;
justify-content: center; // // padding: 1rem;
align-items: center; // // box-sizing: border-box;
height: 600px; // // }
text-align: center;
color: #6b7280; // /* Dashboard Layout - Responsive Grid */
} // .dashboard-layout {
// width: 100% !important;
// height: auto !important;
// min-height: calc(100vh - 2rem);
// background: transparent;
// .e-panel {
// border-radius: 12px;
// box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
// border: 1px solid #e5e7eb;
// background: white;
// transition: all 0.3s ease;
// position: relative;
// overflow: hidden;
// margin: 0;
// &:hover {
// box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
// transform: translateY(-2px);
// }
// }
// .e-panel-header {
// display: none !important; /* Hide panel headers in viewer mode */
// }
// .e-panel-content {
// border-radius: 12px;
// overflow: hidden;
// position: relative;
// width: 100%;
// height: 100%;
// padding: 0;
// margin: 0;
// }
// }
.empty-state-icon { // .dashboard-content-card {
padding: 2rem; // background: white;
background: linear-gradient(to bottom right, #dbeafe, #e0e7ff); // border-radius: 0.75rem;
border-radius: 50%; // box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
margin-bottom: 1.5rem; // border: 1px solid #e5e7eb;
} // min-height: 600px;
// }
.empty-state-icon i { // /* Empty State */
font-size: 3.75rem; // .empty-state {
color: #60a5fa; // display: flex;
} // flex-direction: column;
// justify-content: center;
// align-items: center;
// height: 600px;
// text-align: center;
// color: #6b7280;
// }
.empty-state-title { // .empty-state-icon {
font-size: 1.5rem; // padding: 2rem;
font-weight: 600; // background: linear-gradient(to bottom right, #dbeafe, #e0e7ff);
color: #374151; // border-radius: 50%;
margin-bottom: 0.5rem; // margin-bottom: 1.5rem;
} // }
.empty-state-description { // .empty-state-icon i {
color: #6b7280; // font-size: 3.75rem;
max-width: 28rem; // color: #60a5fa;
margin-bottom: 1.5rem; // }
}
/* Custom Button Styles (fallback for Tailwind) */ // .empty-state-title {
.custom-btn { // font-size: 1.5rem;
display: inline-flex; // font-weight: 600;
align-items: center; // color: #374151;
padding: 0.5rem 1rem; // margin-bottom: 0.5rem;
font-size: 0.875rem; // }
font-weight: 500;
border-radius: 0.5rem;
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
transition: all 0.2s ease-in-out;
border: none;
cursor: pointer;
text-decoration: none;
&:focus {
outline: none;
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.5);
}
&:hover {
transform: translateY(-1px);
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
}
i {
margin-right: 0.5rem;
}
}
.btn-indigo { // .empty-state-description {
background-color: #4f46e5; // color: #6b7280;
color: white; // max-width: 28rem;
// margin-bottom: 1.5rem;
// }
&:hover { // /* Custom Button Styles (fallback for Tailwind) */
background-color: #4338ca; // .custom-btn {
} // display: inline-flex;
// align-items: center;
// padding: 0.5rem 1rem;
// font-size: 0.875rem;
// font-weight: 500;
// border-radius: 0.5rem;
// box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
// transition: all 0.2s ease-in-out;
// border: none;
// cursor: pointer;
// text-decoration: none;
// &:focus {
// outline: none;
// box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.5);
// }
// &:hover {
// transform: translateY(-1px);
// box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
// }
// i {
// margin-right: 0.5rem;
// }
// }
&:focus { // .btn-indigo {
box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.5); // background-color: #4f46e5;
} // color: white;
}
.btn-emerald { // &:hover {
background-color: #10b981; // background-color: #4338ca;
color: white; // }
&:hover { // &:focus {
background-color: #059669; // box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.5);
} // }
// }
&:focus { // .btn-emerald {
box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.5); // background-color: #10b981;
} // color: white;
}
.btn-red { // &:hover {
background-color: #ef4444; // background-color: #059669;
color: white; // }
&:hover { // &:focus {
background-color: #dc2626; // box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.5);
} // }
// }
&:focus { // .btn-red {
box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.5); // background-color: #ef4444;
} // color: white;
}
.btn-green { // &:hover {
background-color: #22c55e; // background-color: #dc2626;
color: white; // }
&:hover { // &:focus {
background-color: #16a34a; // box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.5);
} // }
// }
&:focus { // .btn-green {
box-shadow: 0 0 0 3px rgba(34, 197, 94, 0.5); // background-color: #22c55e;
} // color: white;
}
.btn-blue { // &:hover {
background-color: #3b82f6; // background-color: #16a34a;
color: white; // }
&:hover { // &:focus {
background-color: #2563eb; // box-shadow: 0 0 0 3px rgba(34, 197, 94, 0.5);
} // }
// }
&:focus { // .btn-blue {
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.5); // background-color: #3b82f6;
} // color: white;
}
.btn-gray { // &:hover {
background-color: #6b7280; // background-color: #2563eb;
color: white; // }
&:hover { // &:focus {
background-color: #4b5563; // box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.5);
} // }
// }
&:focus { // .btn-gray {
box-shadow: 0 0 0 3px rgba(107, 114, 128, 0.5); // background-color: #6b7280;
} // color: white;
}
/* Dashboard Layout Styling */ // &:hover {
.dashboard-layout { // background-color: #4b5563;
.e-panel { // }
border-radius: 12px;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
border: 1px solid #e5e7eb;
transition: all 0.3s ease;
position: relative;
max-width: 100%;
overflow: hidden;
&:hover {
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
transform: translateY(-2px);
}
}
.e-panel-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border-radius: 12px 12px 0 0;
padding: 12px 16px;
font-weight: 600;
position: relative;
}
.e-panel-content {
border-radius: 0 0 12px 12px;
overflow: hidden;
position: relative;
}
}
/* Syncfusion DashboardLayout Override for Viewer */ // &:focus {
::ng-deep { // box-shadow: 0 0 0 3px rgba(107, 114, 128, 0.5);
#dashboard_default.e-dashboardlayout { // }
width: 100% !important; // }
height: auto !important;
min-height: calc(100vh - 2rem) !important;
max-height: none !important;
overflow: visible !important;
position: relative !important;
background: transparent !important;
padding: 0 !important;
margin: 0 !important;
.e-panel {
max-width: 100% !important;
overflow: hidden !important;
position: relative !important;
background: white !important;
border: 1px solid #e5e7eb !important;
border-radius: 12px !important;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06) !important;
margin: 0 !important;
}
.e-panel-header {
display: none !important; /* Completely hide headers in viewer */
}
.e-panel-content {
overflow: hidden !important;
max-width: 100% !important;
width: 100% !important;
height: 100% !important;
padding: 0 !important;
margin: 0 !important;
background: white !important;
border-radius: 12px !important;
}
/* Ensure widgets don't overflow */
.e-panel .e-panel-container {
width: 100% !important;
height: 100% !important;
overflow: hidden !important;
position: relative !important;
}
/* Widget content styling */
.e-panel .e-panel-container .content {
width: 100% !important;
height: 100% !important;
overflow: hidden !important;
position: relative !important;
padding: 0 !important;
margin: 0 !important;
}
}
}
/* Widget Panel Enhancements */ // /* Dashboard Layout Styling */
.widget-panel { // .dashboard-layout {
.e-panel { // .e-panel {
background: white; // border-radius: 12px;
border: 1px solid #e5e7eb; // box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
border-radius: 12px; // border: 1px solid #e5e7eb;
overflow: hidden; // transition: all 0.3s ease;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); // position: relative;
// max-width: 100%;
&:hover { // overflow: hidden;
border-color: #3b82f6;
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); // &:hover {
} // box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
} // transform: translateY(-2px);
} // }
// }
// .e-panel-header {
// background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
// color: white;
// border-radius: 12px 12px 0 0;
// padding: 12px 16px;
// font-weight: 600;
// position: relative;
// }
// .e-panel-content {
// border-radius: 0 0 12px 12px;
// overflow: hidden;
// position: relative;
// }
// }
/* Loading States */ // /* Syncfusion DashboardLayout Override for Viewer */
.loading-shimmer { // ::ng-deep {
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%); // #dashboard_default.e-dashboardlayout {
background-size: 200% 100%; // width: 100% !important;
animation: shimmer 2s infinite; // height: auto !important;
} // min-height: calc(100vh - 2rem) !important;
// max-height: none !important;
// overflow: visible !important;
// position: relative !important;
// background: transparent !important;
// padding: 0 !important;
// margin: 0 !important;
// .e-panel {
// max-width: 100% !important;
// overflow: hidden !important;
// position: relative !important;
// background: white !important;
// border: 1px solid #e5e7eb !important;
// border-radius: 12px !important;
// box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06) !important;
// margin: 0 !important;
// }
// .e-panel-header {
// display: none !important; /* Completely hide headers in viewer */
// }
// .e-panel-content {
// overflow: hidden !important;
// max-width: 100% !important;
// width: 100% !important;
// height: 100% !important;
// padding: 0 !important;
// margin: 0 !important;
// background: white !important;
// border-radius: 12px !important;
// }
// /* Ensure widgets don't overflow */
// .e-panel .e-panel-container {
// width: 100% !important;
// height: 100% !important;
// overflow: hidden !important;
// position: relative !important;
// }
// /* Widget content styling */
// .e-panel .e-panel-container .content {
// width: 100% !important;
// height: 100% !important;
// overflow: hidden !important;
// position: relative !important;
// padding: 0 !important;
// margin: 0 !important;
// }
// }
// }
@keyframes shimmer { // /* Widget Panel Enhancements */
0% { background-position: -200% 0; } // .widget-panel {
100% { background-position: 200% 0; } // .e-panel {
} // background: white;
// border: 1px solid #e5e7eb;
// border-radius: 12px;
// overflow: hidden;
// transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
// &:hover {
// border-color: #3b82f6;
// box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
// }
// }
// }
/* Responsive Design for Dashboard Viewer */ // /* Loading States */
@media (max-width: 1200px) { // .loading-shimmer {
.dashboard-layout-container { // background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
padding: 0.75rem; // background-size: 200% 100%;
} // animation: shimmer 2s infinite;
// }
::ng-deep {
#dashboard_default.e-dashboardlayout {
min-height: calc(100vh - 1.5rem) !important;
}
}
}
@media (max-width: 1024px) { // @keyframes shimmer {
.dashboard-layout-container { // 0% { background-position: -200% 0; }
padding: 0.5rem; // 100% { background-position: 200% 0; }
} // }
.dashboard-layout {
.e-panel {
margin: 0.25rem;
}
}
::ng-deep {
#dashboard_default.e-dashboardlayout {
min-height: calc(100vh - 1rem) !important;
.e-panel {
border-radius: 8px !important;
}
}
}
}
@media (max-width: 768px) { // /* Responsive Design for Dashboard Viewer */
.dashboard-layout-container { // @media (max-width: 1200px) {
padding: 0.25rem; // .dashboard-layout-container {
} // padding: 0.75rem;
// }
.dashboard-layout {
.e-panel { // ::ng-deep {
margin: 0.125rem; // #dashboard_default.e-dashboardlayout {
border-radius: 8px; // min-height: calc(100vh - 1.5rem) !important;
} // }
} // }
// }
::ng-deep {
#dashboard_default.e-dashboardlayout {
min-height: calc(100vh - 0.5rem) !important;
.e-panel {
border-radius: 8px !important;
box-shadow: 0 2px 4px -1px rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.06) !important;
}
}
}
}
@media (max-width: 480px) { // @media (max-width: 1024px) {
.dashboard-layout-container { // .dashboard-layout-container {
padding: 0.125rem; // padding: 0.5rem;
} // }
.dashboard-layout { // .dashboard-layout {
.e-panel { // .e-panel {
margin: 0.0625rem; // margin: 0.25rem;
border-radius: 6px; // }
} // }
}
// ::ng-deep {
::ng-deep { // #dashboard_default.e-dashboardlayout {
#dashboard_default.e-dashboardlayout { // min-height: calc(100vh - 1rem) !important;
min-height: calc(100vh - 0.25rem) !important;
// .e-panel {
.e-panel { // border-radius: 8px !important;
border-radius: 6px !important; // }
box-shadow: 0 1px 3px -1px rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.06) !important; // }
} // }
} // }
}
}
// @media (max-width: 768px) {
// .dashboard-layout-container {
// padding: 0.25rem;
// }
// .dashboard-layout {
// .e-panel {
// margin: 0.125rem;
// border-radius: 8px;
// }
// }
// ::ng-deep {
// #dashboard_default.e-dashboardlayout {
// min-height: calc(100vh - 0.5rem) !important;
// .e-panel {
// border-radius: 8px !important;
// box-shadow: 0 2px 4px -1px rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.06) !important;
// }
// }
// }
// }
.dashboard-container { // @media (max-width: 480px) {
display: flex; // .dashboard-layout-container {
height: 100vh; // padding: 0.125rem;
background-color: #f3f4f6; /* bg-gray-100 */ // }
}
// .dashboard-layout {
// .e-panel {
// margin: 0.0625rem;
// border-radius: 6px;
// }
// }
// ::ng-deep {
// #dashboard_default.e-dashboardlayout {
// min-height: calc(100vh - 0.25rem) !important;
// .e-panel {
// border-radius: 6px !important;
// box-shadow: 0 1px 3px -1px rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.06) !important;
// }
// }
// }
// }
.widget-sidebar {
width: 18rem; /* w-72 */
background-color: #ffffff; /* bg-white */
padding: 1rem; /* p-4 */
overflow-y: auto;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); /* shadow-lg */
border-right: 1px solid #e5e7eb; /* border-r border-gray-200 */
display: flex;
flex-direction: column;
}
.widget-list { // .dashboard-container {
flex-grow: 1; // display: flex;
/* space-y-2 is handled by margin-bottom on widget-item */ // height: 100vh;
} // background-color: #f3f4f6; /* bg-gray-100 */
// }
.widget-item { // .widget-sidebar {
padding: 0.75rem; /* p-3 */ // width: 18rem; /* w-72 */
margin-bottom: 0.5rem; /* mb-2 */ // background-color: #ffffff; /* bg-white */
background-color: #f9fafb; /* bg-gray-50 */ // padding: 1rem; /* p-4 */
border-radius: 0.5rem; /* rounded-lg */ // overflow-y: auto;
border: 1px solid #e5e7eb; /* border border-gray-200 */ // box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); /* shadow-lg */
cursor: pointer; // border-right: 1px solid #e5e7eb; /* border-r border-gray-200 */
transition: all 0.2s ease-in-out; // display: flex;
// flex-direction: column;
&:hover { // }
background-color: #e0e7ff; /* hover:bg-indigo-50 */
border-color: #c7d2fe; /* hover:border-indigo-100 */
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); /* hover:shadow-md */
}
}
.dashboard-area { // .widget-list {
flex: 1; // flex-grow: 1;
display: flex; // /* space-y-2 is handled by margin-bottom on widget-item */
flex-direction: column; // }
background-color: #f3f4f6; /* bg-gray-100 */
}
.dashboard-header { // .widget-item {
padding: 1rem; /* p-4 */ // padding: 0.75rem; /* p-3 */
background-color: #ffffff; /* bg-white */ // margin-bottom: 0.5rem; /* mb-2 */
border-bottom: 1px solid #e5e7eb; /* border-b border-gray-200 */ // background-color: #f9fafb; /* bg-gray-50 */
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06); /* shadow-sm */ // border-radius: 0.5rem; /* rounded-lg */
} // border: 1px solid #e5e7eb; /* border border-gray-200 */
// cursor: pointer;
// transition: all 0.2s ease-in-out;
// &:hover {
// background-color: #e0e7ff; /* hover:bg-indigo-50 */
// border-color: #c7d2fe; /* hover:border-indigo-100 */
// box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); /* hover:shadow-md */
// }
// }
.dashboard-content { // .dashboard-area {
// flex: 1; // flex: 1;
padding: 1rem; /* p-4 */ // display: flex;
overflow: auto; // flex-direction: column;
min-height: 600px; /* min-h-[600px] */ // background-color: #f3f4f6; /* bg-gray-100 */
} // }
/* Button styles (assuming ti-btn is a base class) */ // .dashboard-header {
.ti-btn { // padding: 1rem; /* p-4 */
font-weight: bold; // background-color: #ffffff; /* bg-white */
padding: 0.5rem 1rem; /* py-2 px-4 */ // border-bottom: 1px solid #e5e7eb; /* border-b border-gray-200 */
border-radius: 0.5rem; /* rounded-lg */ // box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06); /* shadow-sm */
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); /* shadow-md */ // }
transition: transform 0.2s ease-in-out; /* transition-transform */
transform: scale(1); /* transform */
&:hover {
transform: scale(1.05); /* hover:scale-105 */
}
}
.ti-btn-primary-full { // .dashboard-content {
background-color: #4f46e5; /* bg-indigo-600 */ // // flex: 1;
color: #ffffff; /* text-white */ // padding: 1rem; /* p-4 */
&:hover { // overflow: auto;
background-color: #4338ca; /* hover:bg-indigo-700 */ // min-height: 600px; /* min-h-[600px] */
} // }
}
.ti-btn-info-full { // /* Button styles (assuming ti-btn is a base class) */
background-color: #14b8a6; /* bg-teal-600 */ // .ti-btn {
color: #ffffff; /* text-white */ // font-weight: bold;
&:hover { // padding: 0.5rem 1rem; /* py-2 px-4 */
background-color: #0d9488; /* hover:bg-teal-700 */ // border-radius: 0.5rem; /* rounded-lg */
} // box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); /* shadow-md */
} // transition: transform 0.2s ease-in-out; /* transition-transform */
// transform: scale(1); /* transform */
// &:hover {
// transform: scale(1.05); /* hover:scale-105 */
// }
// }
.ti-btn-danger-full { // .ti-btn-primary-full {
background-color: #ef4444; /* bg-rose-600 */ // background-color: #4f46e5; /* bg-indigo-600 */
color: #ffffff; /* text-white */ // color: #ffffff; /* text-white */
&:hover { // &:hover {
background-color: #dc2626; /* hover:bg-rose-700 */ // background-color: #4338ca; /* hover:bg-indigo-700 */
} // }
} // }
.ti-btn-success-full { // .ti-btn-info-full {
background-color: #10b981; /* bg-emerald-600 */ // background-color: #14b8a6; /* bg-teal-600 */
color: #ffffff; /* text-white */ // color: #ffffff; /* text-white */
&:hover { // &:hover {
background-color: #059669; /* hover:bg-emerald-700 */ // background-color: #0d9488; /* hover:bg-teal-700 */
} // }
} // }
.ti-btn-secondary-full { // .ti-btn-danger-full {
background-color: #6b7280; /* A shade of gray, similar to default secondary */ // background-color: #ef4444; /* bg-rose-600 */
color: #ffffff; // color: #ffffff; /* text-white */
&:hover { // &:hover {
background-color: #4b5563; // background-color: #dc2626; /* hover:bg-rose-700 */
} // }
}
// .control-section {
// margin: 0 auto;
// width: 500px;
// } // }
// #defaultLayout { // .ti-btn-success-full {
// padding: 10px; // background-color: #10b981; /* bg-emerald-600 */
// color: #ffffff; /* text-white */
// &:hover {
// background-color: #059669; /* hover:bg-emerald-700 */
// }
// } // }
// #dashboard_default .e-panel .e-panel-container .content { // .ti-btn-secondary-full {
// vertical-align: middle; // background-color: #6b7280; /* A shade of gray, similar to default secondary */
// font-weight: 600; // color: #ffffff;
// font-size: 20px; // &:hover {
// text-align: center; // background-color: #4b5563;
// line-height: 90px; // }
// } // }
// // .control-section {
// // margin: 0 auto;
// // width: 500px;
// // }
// // #defaultLayout {
// // padding: 10px;
// // }
// // #dashboard_default .e-panel .e-panel-container .content {
// // vertical-align: middle;
// // font-weight: 600;
// // font-size: 20px;
// // text-align: center;
// // line-height: 90px;
// // }
...@@ -163,7 +163,7 @@ export class DashboardViewerComponent implements OnInit, OnDestroy { ...@@ -163,7 +163,7 @@ export class DashboardViewerComponent implements OnInit, OnDestroy {
return null; return null;
} }
return { const panel = {
id: `${(widget as any).instanceId}-${widget.y}-${widget.x}`, id: `${(widget as any).instanceId}-${widget.y}-${widget.x}`,
header: widget.thName, header: widget.thName,
sizeX: widget.cols || 4, // Default size if not specified sizeX: widget.cols || 4, // Default size if not specified
...@@ -177,6 +177,13 @@ export class DashboardViewerComponent implements OnInit, OnDestroy { ...@@ -177,6 +177,13 @@ export class DashboardViewerComponent implements OnInit, OnDestroy {
data: JSON.stringify(dataObject) data: JSON.stringify(dataObject)
}, },
}; };
// Force SyncfusionDatagridWidgetComponent to use full width in viewer
if (widget.component === 'SyncfusionDatagridWidgetComponent') {
panel.sizeX = this.columns;
}
return panel;
}) })
.filter(panel => panel !== null) as DashboardPanel[]; .filter(panel => panel !== null) as DashboardPanel[];
} }
......
...@@ -609,6 +609,10 @@ ...@@ -609,6 +609,10 @@
<div *ngIf="widgetType === 'PieChartWidgetComponent' || widgetType === 'BarChartWidgetComponent' || widgetType === 'AreaChartWidgetComponent' || widgetType === 'DoughnutChartWidgetComponent' || widgetType === 'FunnelChartWidgetComponent'"> <div *ngIf="widgetType === 'PieChartWidgetComponent' || widgetType === 'BarChartWidgetComponent' || widgetType === 'AreaChartWidgetComponent' || widgetType === 'DoughnutChartWidgetComponent' || widgetType === 'FunnelChartWidgetComponent'">
<mat-form-field appearance="fill"> <mat-form-field appearance="fill">
<mat-label>Title</mat-label>
<input matInput [(ngModel)]="currentConfig.title" name="title">
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>X-Axis Field</mat-label> <mat-label>X-Axis Field</mat-label>
<mat-select [(ngModel)]="currentConfig.xField" name="xField"> <mat-select [(ngModel)]="currentConfig.xField" name="xField">
<mat-option *ngFor="let col of availableColumns" [value]="col">{{ col }}</mat-option> <mat-option *ngFor="let col of availableColumns" [value]="col">{{ col }}</mat-option>
...@@ -620,18 +624,26 @@ ...@@ -620,18 +624,26 @@
<mat-option *ngFor="let col of availableColumns" [value]="col">{{ col }}</mat-option> <mat-option *ngFor="let col of availableColumns" [value]="col">{{ col }}</mat-option>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<mat-form-field appearance="fill" *ngIf="widgetType === 'PieChartWidgetComponent' || widgetType === 'DoughnutChartWidgetComponent' || widgetType === 'FunnelChartWidgetComponent'">
<mat-label>Label Field</mat-label>
<mat-select [(ngModel)]="currentConfig.labelField" name="labelField">
<mat-option *ngFor="let col of availableColumns" [value]="col">{{ col }}</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field appearance="fill" *ngIf="widgetType === 'PieChartWidgetComponent' || widgetType === 'DoughnutChartWidgetComponent' || widgetType === 'FunnelChartWidgetComponent'">
<mat-label>Value Field</mat-label>
<mat-select [(ngModel)]="currentConfig.valueField" name="valueField">
<mat-option *ngFor="let col of availableColumns" [value]="col">{{ col }}</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field appearance="fill"> <mat-form-field appearance="fill">
<mat-label>Aggregation (for Pie/Doughnut/Funnel)</mat-label> <mat-label>Aggregation</mat-label>
<mat-select [(ngModel)]="currentConfig.aggregation" name="aggregation"> <mat-select [(ngModel)]="currentConfig.aggregation" name="aggregation">
<mat-option value="none">None</mat-option> <mat-option value="none">None</mat-option>
<mat-option value="count">Count</mat-option> <mat-option value="count">Count</mat-option>
<mat-option value="sum">Sum</mat-option> <mat-option value="sum">Sum</mat-option>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Title</mat-label>
<input matInput [(ngModel)]="currentConfig.title" name="title">
</mat-form-field>
</div> </div>
<div *ngIf="widgetType === 'GaugeChartWidgetComponent'"> <div *ngIf="widgetType === 'GaugeChartWidgetComponent'">
......
...@@ -218,9 +218,11 @@ export class WidgetConfigComponent implements OnInit, AfterViewInit, OnDestroy { ...@@ -218,9 +218,11 @@ export class WidgetConfigComponent implements OnInit, AfterViewInit, OnDestroy {
if (!this.currentConfig.clickAction) this.currentConfig.clickAction = 'none'; if (!this.currentConfig.clickAction) this.currentConfig.clickAction = 'none';
// Layout configuration defaults // Layout configuration defaults
if (this.currentConfig.width === undefined) this.currentConfig.width = 600; if (this.currentConfig.width === undefined) this.currentConfig.width = 100;
if (this.currentConfig.height === undefined) this.currentConfig.height = 400; if (this.currentConfig.height === undefined) this.currentConfig.height = 400;
if (this.currentConfig.responsive === undefined) this.currentConfig.responsive = true; if (this.currentConfig.responsive === undefined) this.currentConfig.responsive = true;
if (this.currentConfig.widthUnit === undefined) this.currentConfig.widthUnit = '%';
if (this.currentConfig.fullWidth === undefined) this.currentConfig.fullWidth = true;
// Data configuration defaults // Data configuration defaults
if (!this.currentConfig.dataSource) this.currentConfig.dataSource = 'static'; if (!this.currentConfig.dataSource) this.currentConfig.dataSource = 'static';
...@@ -257,6 +259,7 @@ export class WidgetConfigComponent implements OnInit, AfterViewInit, OnDestroy { ...@@ -257,6 +259,7 @@ export class WidgetConfigComponent implements OnInit, AfterViewInit, OnDestroy {
if (!this.currentConfig.yAxisField) this.currentConfig.yAxisField = ''; if (!this.currentConfig.yAxisField) this.currentConfig.yAxisField = '';
if (!this.currentConfig.valueField) this.currentConfig.valueField = ''; if (!this.currentConfig.valueField) this.currentConfig.valueField = '';
if (!this.currentConfig.labelField) this.currentConfig.labelField = ''; if (!this.currentConfig.labelField) this.currentConfig.labelField = '';
if (!this.currentConfig.aggregation) this.currentConfig.aggregation = 'count';
if (!this.currentConfig.apiEndpoint) this.currentConfig.apiEndpoint = ''; if (!this.currentConfig.apiEndpoint) this.currentConfig.apiEndpoint = '';
// Chart options defaults // Chart options defaults
......
...@@ -31,18 +31,74 @@ export class BarChartWidgetComponent extends BaseWidgetComponent { ...@@ -31,18 +31,74 @@ export class BarChartWidgetComponent extends BaseWidgetComponent {
} }
onDataUpdate(data: any[]): void { onDataUpdate(data: any[]): void {
if (!data || data.length === 0) { // Check if data is an array
if (!Array.isArray(data) || data.length === 0) {
this.chartData = []; this.chartData = [];
return; return;
} }
const xField = this.configObj.xField || 'x'; // Support multiple field name formats from config
const yField = this.configObj.yField || 'y'; const xField = this.configObj.xField || this.configObj.xAxisField || 'x';
const yField = this.configObj.yField || this.configObj.yAxisField || 'y';
const valueField = this.configObj.valueField || this.configObj.aggregation ? this.configObj.valueField : 'y';
this.chartData = data.map(item => ({ console.log('BarChart onDataUpdate:', {
x: item[xField] || '', data: data.slice(0, 3),
y: item[yField] || 0 xField,
})); yField,
valueField,
aggregation: this.configObj.aggregation,
config: this.configObj
});
// Handle aggregation if needed
// Auto-detect if using ID field
let effectiveAggregation = this.configObj.aggregation;
if (this.configObj.aggregation === 'sum' && valueField) {
const fieldLower = valueField.toLowerCase();
if (fieldLower.includes('id') || (fieldLower.includes('employee') && fieldLower.includes('id'))) {
effectiveAggregation = 'count';
console.warn(`BarChart: Detected ID field "${valueField}", switching from sum to count`);
}
}
if (effectiveAggregation === 'sum' || effectiveAggregation === 'count') {
const groupedData = data.reduce((acc, item) => {
const key = item[xField] || '';
if (!acc[key]) {
acc[key] = 0;
}
if (effectiveAggregation === 'count') {
// Count: เพียงแค่นับจำนวน record
acc[key] += 1;
} else if (effectiveAggregation === 'sum' && valueField && item[valueField]) {
// Sum: ใช้ valueField ในการรวมค่า (เช่น salary, amount)
const value = typeof item[valueField] === 'number' ? item[valueField] : parseFloat(item[valueField]) || 0;
acc[key] += value;
} else {
// ถ้าไม่มี valueField หรือไม่ใช่ number ให้ใช้ count แทน
acc[key] += 1;
}
return acc;
}, {});
this.chartData = Object.keys(groupedData).map(key => ({
x: key,
y: groupedData[key]
}));
} else {
// No aggregation - map directly
const fieldToUse = valueField || yField;
this.chartData = data.map(item => ({
x: item[xField] || '',
y: item[fieldToUse] || 0
}));
}
console.log('BarChart chartData:', this.chartData.slice(0, 5));
} }
onReset(): void { onReset(): void {
......
...@@ -35,22 +35,34 @@ export abstract class BaseWidgetComponent implements OnInit, OnDestroy { ...@@ -35,22 +35,34 @@ export abstract class BaseWidgetComponent implements OnInit, OnDestroy {
next: (selectedDataset: SelectedDataset | null) => { next: (selectedDataset: SelectedDataset | null) => {
this.isLoading = true; this.isLoading = true;
this.hasError = false; this.hasError = false;
if (selectedDataset && selectedDataset.data) {
console.log('BaseWidget received dataset:', {
selectedDataset,
hasData: selectedDataset?.data?.length,
widgetConfig: this.configObj
});
if (selectedDataset && selectedDataset.data && Array.isArray(selectedDataset.data)) {
try { try {
this.originalData = selectedDataset.data; this.originalData = selectedDataset.data;
console.log('BaseWidget applying filters on data of length:', this.originalData.length);
this.applyFilters(); this.applyFilters();
console.log('BaseWidget filtered data length:', this.filteredData.length);
this.onDataUpdate(this.filteredData); this.onDataUpdate(this.filteredData);
this.isLoading = false; this.isLoading = false;
} catch (error) { } catch (error) {
console.error('BaseWidget error in data processing:', error);
this.handleError(error); this.handleError(error);
} }
} else { } else {
console.warn('BaseWidget no dataset available, showing loading state');
// If no dataset is selected, just keep showing the initial config with a loading state. // If no dataset is selected, just keep showing the initial config with a loading state.
// The initial state is set by applyInitialConfig(). // The initial state is set by applyInitialConfig().
this.isLoading = true; this.isLoading = true;
} }
}, },
error: (err) => { error: (err) => {
console.error('BaseWidget error in subscription:', err);
this.handleError(err); this.handleError(err);
} }
}); });
...@@ -105,7 +117,9 @@ export abstract class BaseWidgetComponent implements OnInit, OnDestroy { ...@@ -105,7 +117,9 @@ export abstract class BaseWidgetComponent implements OnInit, OnDestroy {
// Parse data string to array // Parse data string to array
try { try {
this.originalData = JSON.parse(this.data); const parsedData = JSON.parse(this.data);
// Ensure parsed data is an array, not an object
this.originalData = Array.isArray(parsedData) ? parsedData : [];
} catch (error) { } catch (error) {
console.warn('Failed to parse data JSON:', error); console.warn('Failed to parse data JSON:', error);
this.originalData = []; this.originalData = [];
......
...@@ -21,33 +21,102 @@ export class PieChartWidgetComponent extends BaseWidgetComponent { ...@@ -21,33 +21,102 @@ export class PieChartWidgetComponent extends BaseWidgetComponent {
applyInitialConfig(): void { applyInitialConfig(): void {
this.title = this.configObj.title || 'Pie Chart'; this.title = this.configObj.title || 'Pie Chart';
this.legendSettings = { visible: true }; this.legendSettings = {
visible: true,
position: 'Bottom',
textStyle: { size: '14px' },
height: '50px',
width: '100%',
alignment: 'Center'
};
this.chartData = []; this.chartData = [];
} }
onDataUpdate(data: any[]): void { onDataUpdate(data: any[]): void {
if (!data || data.length === 0) { // Check if data is an array
if (!Array.isArray(data) || data.length === 0) {
this.chartData = []; this.chartData = [];
return; return;
} }
const xField = this.configObj.xField || 'x'; // Support multiple field name formats from config
const yField = this.configObj.yField || 'y'; const labelField = this.configObj.labelField || this.configObj.xField || this.configObj.xAxisField || 'x';
const valueField = this.configObj.valueField || this.configObj.yField || this.configObj.yAxisField || 'y';
console.log('PieChart onDataUpdate:', {
title: this.title,
dataLength: data.length,
labelField,
valueField,
aggregation: this.configObj.aggregation,
sampleData: data.slice(0, 2)
});
let transformedData = data; let transformedData = data;
if (this.configObj.aggregation === 'count') { // Handle aggregation if needed
// Default to count if using any id field as value
let effectiveAggregation = this.configObj.aggregation;
if (this.configObj.aggregation === 'sum' && valueField) {
const fieldLower = valueField.toLowerCase();
if (fieldLower.includes('id') ||
fieldLower.includes('employee') && fieldLower.includes('id')) {
effectiveAggregation = 'count';
console.warn(`Detected ID field "${valueField}", switching from sum to count aggregation`);
}
}
console.log('Effective aggregation:', effectiveAggregation, 'original:', this.configObj.aggregation, 'labelField:', labelField, 'valueField:', valueField);
if (effectiveAggregation === 'count' || (effectiveAggregation === 'sum' && !valueField)) {
// Count: นับจำนวนรายการตาม labelField
// หรือ Sum ถ้าไม่ระบุ valueField (คล้าย count)
const counts = transformedData.reduce((acc, item) => { const counts = transformedData.reduce((acc, item) => {
const key = item[xField] || ''; const key = item[labelField] || '';
acc[key] = (acc[key] || 0) + 1; acc[key] = (acc[key] || 0) + 1;
return acc; return acc;
}, {}); }, {});
transformedData = Object.keys(counts).map(key => ({ x: key, y: counts[key] })); transformedData = Object.keys(counts).map(key => ({
x: key || '(Empty)',
y: counts[key],
text: `${key || '(Empty)'}: ${counts[key]}`
}));
console.log('PieChart using COUNT aggregation, result:', transformedData);
} else if (effectiveAggregation === 'sum' && valueField) {
// Sum: รวมค่า valueField ตาม labelField (เฉพาะกรณีที่ระบุ valueField)
// ตรวจสอบว่า valueField มีค่าที่สมเหตุสมผลหรือไม่
const groupedData = transformedData.reduce((acc, item) => {
const key = item[labelField] || '';
const value = item[valueField] || 0;
if (!acc[key]) {
acc[key] = 0;
}
// ถ้า value เป็น ID หรือ string ที่ไม่ได้เป็นตัวเลข ให้ไม่ sum
if (typeof value === 'string' && isNaN(Number(value))) {
// ถ้าเป็น ID หรือ string ธรรมดา ให้ข้าม
return acc;
}
acc[key] += typeof value === 'number' ? value : parseFloat(value) || 0;
return acc;
}, {});
transformedData = Object.keys(groupedData).map(key => ({
x: key || '(Empty)',
y: groupedData[key],
text: `${key || '(Empty)'}: ${groupedData[key]}`
}));
} else { } else {
// No aggregation: map directly
transformedData = transformedData.map(item => ({ transformedData = transformedData.map(item => ({
x: item[xField] || '', x: item[labelField] || '(Empty)',
y: item[yField] || 0 y: item[valueField] || 0,
text: `${item[labelField] || '(Empty)'}: ${item[valueField] || 0}`
})); }));
} }
console.log('PieChart chartData:', transformedData.slice(0, 10));
this.chartData = transformedData; this.chartData = transformedData;
} }
......
...@@ -325,17 +325,37 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent { ...@@ -325,17 +325,37 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent {
return; return;
} }
console.log('SimpleKpiWidget onDataUpdate:', {
title: this.title,
dataLength: data?.length,
hasFilter: this.enableFilter,
filterField: this.filterField,
filterValue: this.filterValue
});
// Transform data if transform function is provided // Transform data if transform function is provided
let transformedData = this.transformData(data); let transformedData = this.transformData(data);
// Apply filtering if enabled // Apply filtering if enabled
if (this.enableFilter && this.filterField && this.filterValue) { if (this.enableFilter && this.filterField && this.filterValue) {
transformedData = this.applyFilter(transformedData); transformedData = this.applyFilter(transformedData);
console.log('SimpleKpiWidget after filter:', {
filteredLength: transformedData?.length
});
} }
// Handle count aggregation separately as it doesn't need a valueField // Handle count aggregation separately as it doesn't need a valueField
if (this.aggregation === 'count') { if (this.aggregation === 'count') {
this.value = (transformedData?.length || 0).toLocaleString(); // If valueField is specified and it's a date/unique field, count unique values
if (this.valueField && ['dateid', 'date', 'datetime', 'day'].some(part => this.valueField.toLowerCase().includes(part))) {
// Count unique values instead of all records
const uniqueValues = new Set(transformedData.map(item => item[this.valueField]));
this.value = uniqueValues.size.toLocaleString();
console.log(`SimpleKpiWidget counting unique ${this.valueField}: ${uniqueValues.size} from ${transformedData.length} records`);
} else {
// Regular count: count all records
this.value = (transformedData?.length || 0).toLocaleString();
}
this.updateTrendData(transformedData); this.updateTrendData(transformedData);
this.updateLabel(transformedData); this.updateLabel(transformedData);
return; return;
...@@ -914,15 +934,22 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent { ...@@ -914,15 +934,22 @@ export class SimpleKpiWidgetComponent extends BaseWidgetComponent {
return data; return data;
} }
console.log('SimpleKpiWidget applying filter:', {
filterField: this.filterField,
filterValue: this.filterValue,
filterOperator: this.filterOperator,
dataLength: data?.length
});
return data.filter(item => { return data.filter(item => {
const fieldValue = item[this.filterField]; const fieldValue = item[this.filterField];
const filterValue = this.filterValue; const filterValue = this.filterValue;
switch (this.filterOperator) { switch (this.filterOperator) {
case 'equals': case 'equals':
return fieldValue == filterValue; return String(fieldValue).toLowerCase() === String(filterValue).toLowerCase();
case 'not_equals': case 'not_equals':
return fieldValue != filterValue; return String(fieldValue).toLowerCase() !== String(filterValue).toLowerCase();
case 'greater_than': case 'greater_than':
return Number(fieldValue) > Number(filterValue); return Number(fieldValue) > Number(filterValue);
case 'less_than': case 'less_than':
......
...@@ -115,6 +115,7 @@ ...@@ -115,6 +115,7 @@
(actionComplete)="actionComplete($event)" (actionComplete)="actionComplete($event)"
(dataBound)="onDataBound($event)" (dataBound)="onDataBound($event)"
[height]="'100%'" [height]="'100%'"
[width]="'100%'"
[allowResizing]="allowResizing" [allowResizing]="allowResizing"
[rowHeight]="rowHeight"> [rowHeight]="rowHeight">
......
...@@ -5,6 +5,9 @@ ...@@ -5,6 +5,9 @@
height: 100%; height: 100%;
position: relative; position: relative;
overflow: hidden; overflow: hidden;
width: 100%;
margin: 0;
padding: 0;
// Widget Header // Widget Header
.widget-header { .widget-header {
...@@ -300,6 +303,10 @@ ...@@ -300,6 +303,10 @@
background: white; background: white;
border-radius: 8px; border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
width: 100%;
height: 100%;
margin: 0;
padding: 0;
} }
// Interaction States // Interaction States
...@@ -454,10 +461,18 @@ ...@@ -454,10 +461,18 @@
.e-grid { .e-grid {
border: none !important; border: none !important;
font-family: inherit; font-family: inherit;
width: 100% !important;
height: 100% !important;
min-width: 0 !important;
min-height: 0 !important;
margin: 0 !important;
padding: 0 !important;
max-width: 100% !important;
.e-gridheader { .e-gridheader {
background-color: inherit; background-color: inherit;
border-bottom: 1px solid rgba(229, 231, 235, 0.5); border-bottom: 1px solid rgba(229, 231, 235, 0.5);
width: 100% !important;
.e-headercell { .e-headercell {
background-color: inherit; background-color: inherit;
...@@ -466,6 +481,10 @@ ...@@ -466,6 +481,10 @@
font-size: 13px; font-size: 13px;
padding: 12px 8px; padding: 12px 8px;
border-right: 1px solid rgba(229, 231, 235, 0.3); border-right: 1px solid rgba(229, 231, 235, 0.3);
min-width: 0 !important;
overflow: hidden !important;
text-overflow: ellipsis !important;
white-space: nowrap !important;
&:last-child { &:last-child {
border-right: none; border-right: none;
...@@ -475,6 +494,8 @@ ...@@ -475,6 +494,8 @@
.e-content { .e-content {
background-color: transparent; background-color: transparent;
width: 100% !important;
min-width: 0 !important;
.e-row { .e-row {
border-bottom: 1px solid rgba(229, 231, 235, 0.2); border-bottom: 1px solid rgba(229, 231, 235, 0.2);
...@@ -497,6 +518,10 @@ ...@@ -497,6 +518,10 @@
border-right: 1px solid rgba(229, 231, 235, 0.2); border-right: 1px solid rgba(229, 231, 235, 0.2);
font-size: 13px; font-size: 13px;
color: inherit; color: inherit;
min-width: 0 !important;
overflow: hidden !important;
text-overflow: ellipsis !important;
white-space: nowrap !important;
&:last-child { &:last-child {
border-right: none; border-right: none;
......
...@@ -167,7 +167,7 @@ export class SyncfusionDatagridWidgetComponent extends BaseWidgetComponent imple ...@@ -167,7 +167,7 @@ export class SyncfusionDatagridWidgetComponent extends BaseWidgetComponent imple
public animateConditionalChange: boolean = true; public animateConditionalChange: boolean = true;
// Layout properties // Layout properties
public width: number = 600; public width: number = 100;
public height: number = 400; public height: number = 400;
public minWidth: number = 400; public minWidth: number = 400;
public minHeight: number = 300; public minHeight: number = 300;
...@@ -175,11 +175,11 @@ export class SyncfusionDatagridWidgetComponent extends BaseWidgetComponent imple ...@@ -175,11 +175,11 @@ export class SyncfusionDatagridWidgetComponent extends BaseWidgetComponent imple
public maxHeight: number = 800; public maxHeight: number = 800;
public aspectRatio: string = 'auto'; public aspectRatio: string = 'auto';
public responsive: boolean = true; public responsive: boolean = true;
public widthUnit: string = 'px'; public widthUnit: string = '%';
public heightUnit: string = 'px'; public heightUnit: string = 'px';
public fullWidth: boolean = false; public fullWidth: boolean = true;
public fullHeight: boolean = false; public fullHeight: boolean = false;
public sizeOption: string = 'medium'; public sizeOption: string = 'full-width';
// Data properties // Data properties
public dataSource: string = 'static'; public dataSource: string = 'static';
...@@ -392,7 +392,7 @@ export class SyncfusionDatagridWidgetComponent extends BaseWidgetComponent imple ...@@ -392,7 +392,7 @@ export class SyncfusionDatagridWidgetComponent extends BaseWidgetComponent imple
this.customClickHandler = this.configObj.customClickHandler || ''; this.customClickHandler = this.configObj.customClickHandler || '';
// Layout configuration // Layout configuration
this.width = this.configObj.width || 600; this.width = this.configObj.width || 100;
this.height = this.configObj.height || 400; this.height = this.configObj.height || 400;
this.minWidth = this.configObj.minWidth || 400; this.minWidth = this.configObj.minWidth || 400;
this.minHeight = this.configObj.minHeight || 300; this.minHeight = this.configObj.minHeight || 300;
...@@ -400,9 +400,9 @@ export class SyncfusionDatagridWidgetComponent extends BaseWidgetComponent imple ...@@ -400,9 +400,9 @@ export class SyncfusionDatagridWidgetComponent extends BaseWidgetComponent imple
this.maxHeight = this.configObj.maxHeight || 800; this.maxHeight = this.configObj.maxHeight || 800;
this.aspectRatio = this.configObj.aspectRatio || 'auto'; this.aspectRatio = this.configObj.aspectRatio || 'auto';
this.responsive = this.configObj.responsive !== undefined ? this.configObj.responsive : true; this.responsive = this.configObj.responsive !== undefined ? this.configObj.responsive : true;
this.widthUnit = this.configObj.widthUnit || 'px'; this.widthUnit = this.configObj.widthUnit || '%';
this.heightUnit = this.configObj.heightUnit || 'px'; this.heightUnit = this.configObj.heightUnit || 'px';
this.fullWidth = this.configObj.fullWidth !== undefined ? this.configObj.fullWidth : false; this.fullWidth = this.configObj.fullWidth !== undefined ? this.configObj.fullWidth : true;
this.fullHeight = this.configObj.fullHeight !== undefined ? this.configObj.fullHeight : false; this.fullHeight = this.configObj.fullHeight !== undefined ? this.configObj.fullHeight : false;
this.sizeOption = this.configObj.sizeOption || 'medium'; this.sizeOption = this.configObj.sizeOption || 'medium';
...@@ -843,8 +843,8 @@ export class SyncfusionDatagridWidgetComponent extends BaseWidgetComponent imple ...@@ -843,8 +843,8 @@ export class SyncfusionDatagridWidgetComponent extends BaseWidgetComponent imple
'border-color': this.borderColor, 'border-color': this.borderColor,
'border-radius': `${this.borderRadius}px`, 'border-radius': `${this.borderRadius}px`,
'border-width': `${this.borderWidth}px`, 'border-width': `${this.borderWidth}px`,
'padding': `${this.padding}px`, 'padding': '0px', // Force no padding for full width
'margin': `${this.margin}px`, 'margin': '0px', // Force no margin for full width
'font-size': `${this.fontSize}px`, 'font-size': `${this.fontSize}px`,
'font-weight': this.fontWeight, 'font-weight': this.fontWeight,
'font-family': this.fontFamily, 'font-family': this.fontFamily,
......
...@@ -469,11 +469,11 @@ export class SyncfusionPivotWidgetComponent extends BaseWidgetComponent implemen ...@@ -469,11 +469,11 @@ export class SyncfusionPivotWidgetComponent extends BaseWidgetComponent implemen
onDataBound(args: any): void { onDataBound(args: any): void {
// Apply perspective after data is loaded and rendered, but only once. // Apply perspective after data is loaded and rendered, but only once.
if (this.perspective && !this.isPerspectiveApplied && this.perspective !== '{}') { // if (this.perspective && !this.isPerspectiveApplied && this.perspective !== '{}') {
setTimeout(() => { // setTimeout(() => {
this.setWidgetState(this.perspective as string); // this.setWidgetState(this.perspective as string);
}, 50); // Small delay to ensure rendering is complete // }, 50); // Small delay to ensure rendering is complete
} // }
} }
onReset(): void { onReset(): void {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment