From cc44826f669be9dc0fb6c8c163c5278d3149b651 Mon Sep 17 00:00:00 2001 From: Nalvazhuthi Date: Tue, 29 Apr 2025 10:34:21 +0530 Subject: [PATCH] updating progressbar --- app/src/components/icons/analysis.tsx | 93 ++++++ .../ui/analysis/ProductionCapacity.tsx | 62 ++++ app/src/components/ui/analysis/ROISummary.tsx | 151 ++++++++++ .../ui/analysis/SemiCircleProgress.tsx | 25 ++ .../ui/analysis/ThroughputSummary.tsx | 146 ++++++++++ .../visualization/RealTimeVisulization.tsx | 2 +- app/src/pages/Project.tsx | 12 +- .../components/analysis/ROISummary.scss | 269 +++++++++++++++++ .../styles/components/analysis/analysis.scss | 270 ++++++++++++++++++ app/src/styles/main.scss | 2 + 10 files changed, 1029 insertions(+), 3 deletions(-) create mode 100644 app/src/components/icons/analysis.tsx create mode 100644 app/src/components/ui/analysis/ProductionCapacity.tsx create mode 100644 app/src/components/ui/analysis/ROISummary.tsx create mode 100644 app/src/components/ui/analysis/SemiCircleProgress.tsx create mode 100644 app/src/components/ui/analysis/ThroughputSummary.tsx create mode 100644 app/src/styles/components/analysis/ROISummary.scss create mode 100644 app/src/styles/components/analysis/analysis.scss diff --git a/app/src/components/icons/analysis.tsx b/app/src/components/icons/analysis.tsx new file mode 100644 index 0000000..3a5542b --- /dev/null +++ b/app/src/components/icons/analysis.tsx @@ -0,0 +1,93 @@ +export function ThroughputSummaryIcon() { + return ( + + + + + ); +} +export function ProductionCapacityIcon() { + return ( + + + + + ); +} +export function ROISummaryIcon() { + return ( + + + + + + ); +} +export function PowerIcon() { + return ( + + + + + + + + + + + + ); +} diff --git a/app/src/components/ui/analysis/ProductionCapacity.tsx b/app/src/components/ui/analysis/ProductionCapacity.tsx new file mode 100644 index 0000000..268ea81 --- /dev/null +++ b/app/src/components/ui/analysis/ProductionCapacity.tsx @@ -0,0 +1,62 @@ +import React from "react"; +import { ProductionCapacityIcon } from "../../icons/analysis"; + +const ProductionCapacity = () => { + const totalBars = 6; + const progressPercent = 50; + + const barsToFill = Math.floor((progressPercent / 100) * totalBars); + const partialFillPercent = + ((progressPercent / 100) * totalBars - barsToFill) * 100; + + return ( +
+
+
+
+
Throughput Summary
+
08:00 - 09:00 AM
+
+
+ +
+
+ +
+
+ 128 Units/hour +
+ + {/* Progress Bar */} +
+ {[...Array(totalBars)].map((_, i) => ( +
+ {i < barsToFill ? ( +
+ ) : i === barsToFill ? ( +
+ ) : null} +
+ ))} +
+
+ +
+
+ Avg. Process Time + 28.4 Secs/unit +
+
+ Machine Utilization + 78% +
+
+
+
+ ); +}; + +export default ProductionCapacity; diff --git a/app/src/components/ui/analysis/ROISummary.tsx b/app/src/components/ui/analysis/ROISummary.tsx new file mode 100644 index 0000000..cacd2c7 --- /dev/null +++ b/app/src/components/ui/analysis/ROISummary.tsx @@ -0,0 +1,151 @@ +import React from "react"; +import { ROISummaryIcon } from "../../icons/analysis"; +import SemiCircleProgress from "./SemiCircleProgress"; + +const ROISummary = () => { + // Data for the cost breakdown as an array of objects + const costBreakdownData = [ + { + item: "Raw Material A", + unitCost: "₹ 10/unit", + laborCost: "₹ 0", + totalCost: "₹ 1000", + sellingPrice: "₹ 1500", + }, + { + item: "Labor", + unitCost: "₹ 10/unit", + laborCost: "₹ 500", + totalCost: "₹ 500", + sellingPrice: "N/A", + }, + { + item: "Product 1", + unitCost: "₹ 10/unit", + laborCost: "₹ 200", + totalCost: "₹ 200", + sellingPrice: "₹ 2000", + }, + { + item: "Machine", + unitCost: "-", + laborCost: "-", + totalCost: "₹ 20,000", + sellingPrice: "N/A", + }, + { + item: "Total", + unitCost: "-", + laborCost: "-", + totalCost: "₹ 1,20,000", + sellingPrice: "-", + }, + { + item: "Net Profit", + unitCost: "-", + laborCost: "-", + totalCost: "₹ 1,60,000", + sellingPrice: "-", + }, + ]; + const progressValue = 50; + return ( +
+
+
+
+
ROI Summary
+
From 24 November, 2025
+
+
+ +
+
+
+
Product :
+
Product name
+
+
+
+
+ +133% ROI with payback in just 50.3 months +
+
+
+ + +
+
+
+ Total Cost Incurred + ₹ 1,20,000 +
+
+ Revenue Generated + ₹ 2,80,000 +
+
+
+ Net Profit + ₹ 1,60,000 +
+
+
+
+
+ + Cost Breakdown + +
+ + + + + + + + + + + + {costBreakdownData.map((row, index) => ( + + + + + + + + ))} + +
ItemUnit CostLabor CostTotal CostSelling Price
{row.item}{row.unitCost}{row.laborCost}{row.totalCost}{row.sellingPrice}
+
+
+
+ 💡 + How to improve ROI? +
+
+ Increase CNC utilization by 10%{" "} + to shave 0.5 months of payback + period +
+ +
+
+
+ ); +}; + +export default ROISummary; diff --git a/app/src/components/ui/analysis/SemiCircleProgress.tsx b/app/src/components/ui/analysis/SemiCircleProgress.tsx new file mode 100644 index 0000000..bd11d28 --- /dev/null +++ b/app/src/components/ui/analysis/SemiCircleProgress.tsx @@ -0,0 +1,25 @@ +import React from "react"; + +const SemiCircleProgress = () => { + const progress = 10; + const clampedProgress = Math.min(Math.max(progress, 0), 100); // clamp 0-100 + return ( +
+
+
+
+
{clampedProgress}%
+
+ ); +}; + +export default SemiCircleProgress; diff --git a/app/src/components/ui/analysis/ThroughputSummary.tsx b/app/src/components/ui/analysis/ThroughputSummary.tsx new file mode 100644 index 0000000..cb4fac5 --- /dev/null +++ b/app/src/components/ui/analysis/ThroughputSummary.tsx @@ -0,0 +1,146 @@ +import React from "react"; +import { Line } from "react-chartjs-2"; +import { + Chart as ChartJS, + LineElement, + CategoryScale, + LinearScale, + PointElement, +} from "chart.js"; +import { PowerIcon, ThroughputSummaryIcon } from "../../icons/analysis"; + +ChartJS.register(LineElement, CategoryScale, LinearScale, PointElement); + +const ThroughputSummary = () => { + const data = { + labels: ["08:00", "08:10", "08:20", "08:30", "08:40", "08:50", "09:00"], + datasets: [ + { + label: "Units/hour", + data: [100, 120, 110, 130, 125, 128, 132], + borderColor: "#B392F0", + tension: 0.4, + pointRadius: 0, // hide points + }, + ], + }; + + const options = { + responsive: true, + scales: { + x: { + grid: { + display: false, + }, + ticks: { + display: false, + color: "#fff", + }, + }, + y: { + grid: { + display: false, + }, + ticks: { + display: false, + color: "#fff", + }, + }, + }, + plugins: { + legend: { + display: false, + }, + tooltip: { + enabled: true, + }, + }, + }; + + const shiftUtilization = { + "shift 1": 25, + "shift 2": 45, + "shift 3": 15, + }; + + return ( +
+
+
+
+
Throughput Summary
+
08:00 - 09:00 AM
+
+
+ +
+
+ +
+
+ 1240 Units/hour +
+
+
+
Asset usage
+
85%
+
+ +
+
+ +
+
+
Energy Consumption
+
+
+ +
+
+
456
+
KWH
+
+
+
+
+
Shift Utilization
+
+
85%
+ +
+
+
+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+
+
+ ); +}; + +export default ThroughputSummary; diff --git a/app/src/modules/visualization/RealTimeVisulization.tsx b/app/src/modules/visualization/RealTimeVisulization.tsx index cd2c57c..50b72ba 100644 --- a/app/src/modules/visualization/RealTimeVisulization.tsx +++ b/app/src/modules/visualization/RealTimeVisulization.tsx @@ -343,7 +343,7 @@ const RealTimeVisulization: React.FC = () => { onDrop={(event) => handleDrop(event)} onDragOver={(event) => event.preventDefault()} > - + {/* */}
{activeModule === "visualization" && selectedZone.zoneName !== "" && ( diff --git a/app/src/pages/Project.tsx b/app/src/pages/Project.tsx index 2daa091..26d1d7d 100644 --- a/app/src/pages/Project.tsx +++ b/app/src/pages/Project.tsx @@ -23,6 +23,9 @@ import SimulationPlayer from "../components/ui/simulation/simulationPlayer"; import RenderOverlay from "../components/templates/Overlay"; import MenuBar from "../components/ui/menu/menu"; import KeyPressListener from "../utils/shortcutkeys/handleShortcutKeys"; +import ProductionCapacity from "../components/ui/analysis/ProductionCapacity"; +import ThroughputSummary from "../components/ui/analysis/ThroughputSummary"; +import ROISummary from "../components/ui/analysis/ROISummary"; const Project: React.FC = () => { let navigate = useNavigate(); @@ -38,7 +41,7 @@ const Project: React.FC = () => { setFloorItems([]); setWallItems([]); setZones([]); - setActiveModule('builder') + setActiveModule("builder"); const email = localStorage.getItem("email"); if (email) { const Organization = email!.split("@")[1].split(".")[0]; @@ -57,8 +60,13 @@ const Project: React.FC = () => { return (
+
+ {/* + */} + +
- {loadingProgress && } + {/* {loadingProgress && } */} {!isPlaying && ( <> {toggleThreeD && } diff --git a/app/src/styles/components/analysis/ROISummary.scss b/app/src/styles/components/analysis/ROISummary.scss new file mode 100644 index 0000000..f4ee414 --- /dev/null +++ b/app/src/styles/components/analysis/ROISummary.scss @@ -0,0 +1,269 @@ +.roiSummary-container { + + .roiSummary-wrapper { + background-color: #F2F2F7; + + .product-info { + display: flex; + } + + .playBack { + display: flex; + background-color: var(--background-color); + border-radius: 12px; + padding: 6px; + + .info { + span { + font-size: var(--font-size-xlarge); + + &:first-child { + color: #31C756; + } + + &:last-child { + color: #2B3344; + } + } + } + } + + .roi-details { + display: flex; + + .roi-progress { + width: 60%; + } + + .metrics { + display: flex; + flex-direction: column; + gap: 6px; + + .metric-item { + width: 100%; + border-radius: 6px; + border: 1px solid var(--axis-colors-green, #43C06D); + background: var(--axis-colors-green-lite, #BEEECF); + display: flex; + flex-direction: column; + padding: 4px 6px; + + &:last-child { + align-items: center; + } + + .metric-label { + font-size: 10px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + + } + + .metric-value { + text-align: center; + line-height: 20px; + } + } + + .metric-wrapper { + display: flex; + gap: 6px; + + .metric-item { + background-color: var(--background-color); + border: 1px solid var(--Grays-Gray-6, #F2F2F7); + + } + } + } + } + + + .cost-breakdown { + background-color: var(--background-color); + border: 1px solid var(--text-disabled); + border-radius: 8px; + padding: 16px; + + .breakdown-header { + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 16px; + + .section-number { + font-size: 20px; + color: #00aaff; + } + + .section-title { + font-size: var(--font-size-regular); + color: #333; + } + + .expand-icon { + font-size: 16px; + color: #666; + cursor: pointer; + } + } + + + + .breakdown-table { + width: 100%; + border-collapse: collapse; + border-radius: 8px; + + th, + td { + padding: 8px; + text-align: left; + border-top: 1px solid var(--text-disabled); + border-bottom: 1px solid var(--text-disabled); + } + + /* Apply left border to first child */ + th:first-child { + border-left: 1px solid var(--text-disabled); + } + + /* Apply right border to last child */ + th:last-child { + border-right: 1px solid var(--text-disabled); + } + + td:first-child { + border-left: 1px solid var(--text-disabled); + } + + /* Apply right border to last child */ + td:last-child { + border-right: 1px solid var(--text-disabled); + } + + th { + background-color: var(--background-color); + + color: #333; + } + + .total-row, + .net-profit-row { + font-weight: bold; + color: #333; + } + } + + + + } + + + .tips-section { + background-color: var(--background-color); + border-radius: 8px; + display: flex; + flex-direction: column; + gap: 6px; + padding: 12px; + + .tip-header { + display: flex; + align-items: center; + + .tip-title { + color: var(--text-color); + font-weight: 600; + } + } + + .tip-description { + span { + color: #34C759; + /* Default color for the first span */ + + &:first-child { + color: #34C759; + /* Color for the first span */ + } + + &:nth-child(2) { + color: #488EF6; + /* Color for the second span */ + } + } + } + } + + .get-tips-button { + border: none; + + border-radius: 5px; + cursor: pointer; + font-size: 14px; + margin-top: 8px; + + /* Make the button content-width dependent */ + display: inline-block; + display: flex; + justify-content: flex-end; + background: none; + + .btn { + background-color: var(--accent-color); + color: var(--background-color); + padding: 4px 6px; + /* Add padding to ensure it has space around the text */ + border-radius: 5px; + display: inline-block; + /* Ensure button width adjusts to its content */ + font-size: 14px; + text-align: center; + /* Ensure text is centered */ + } + } + + + + } +} + + +.semi-circle-wrapper { + width: 250px; + height: 125px; + overflow: hidden; + position: relative; + } + + .semi-circle { + width: 250px; + height: 250px; + border-radius: 50%; + position: relative; + transition: background 0.5s ease; + transform: rotate(180deg); /* rotate so 0% is at left */ + } + + .progress-cover { + position: absolute; + width: 75%; + height: 75%; + top: 12.5%; + left: 12.5%; + background-color: white; + border-radius: 50%; + } + + .label { + position: absolute; + top: 40%; + left: 50%; + transform: translate(-50%, -50%); + font-weight: bold; + font-size: 1.2rem; + } + \ No newline at end of file diff --git a/app/src/styles/components/analysis/analysis.scss b/app/src/styles/components/analysis/analysis.scss new file mode 100644 index 0000000..bc33556 --- /dev/null +++ b/app/src/styles/components/analysis/analysis.scss @@ -0,0 +1,270 @@ +.analysis { + position: absolute; + top: 0; + left: 0; + display: flex; + justify-content: center; + align-items: center; + width: 100%; + height: 100vh; + z-index: 100000000000000000000000000000; +} + +.analysis-card { + min-width: 333px; + // background: var(--primary-color); + border-radius: 20px; + + padding: 8px; + + .analysis-card-wrapper { + background: var(--background-color); + border-radius: 14px; + padding: 16px; + + display: flex; + flex-direction: column; + gap: 14px; + + .card-header { + width: 100%; + display: flex; + justify-content: space-between; + align-items: center; + + .main-header { + line-height: 20px; + font-size: var(--font-size-regular); + } + } + + .process-container { + display: flex; + flex-direction: column; + + .throughput-value { + font-size: 1rem; + + .value { + font-weight: bold; + font-size: 1.5rem; + } + } + + .progress-bar-wrapper { + display: flex; + gap: 8px; + margin-top: 6px; + } + + .progress-bar { + position: relative; + width: 36px; + height: 4px; + border-radius: 13px; + overflow: hidden; + background-color: #FBEBD7; + + .bar-fill { + position: absolute; + height: 100%; + top: 0; + left: 0; + background-color: #FC9D2F; + border-radius: 13px; + } + + .bar-fill.full { + width: 100%; + } + + .bar-fill.partial { + width: 0; // inline style will override this + } + } + } + + .metrics-section { + padding-top: 16px; + border-top: 1px solid var(--background-color-gray); + + .metric { + display: flex; + justify-content: space-between; + align-items: center; + font-size: 14px; + margin-bottom: 8px; + + .label { + color: var(--text-color); + } + + .value { + font-weight: bold; + } + } + } + } +} + + +.throughoutSummary { + .throughoutSummary-wrapper { + .process-container { + display: flex; + flex-direction: row; + align-items: center; + justify-content: flex-start; + gap: 16px; + width: 100%; + + .throughput-value { + font-size: var(--font-size-small); + flex: 1; + display: flex; + flex-direction: column; + + .value { + color: var(--accent-color); + } + + /* Let the text take available space */ + } + + .lineChart { + max-width: 200px; + height: 100px; + position: relative; + + .assetUsage { + text-align: right; + position: absolute; + right: 0; + top: 0; + } + + canvas { + background-color: transparent; + } + } + } + + .footer { + display: flex; + gap: 16px; // Space between cards + margin-top: 24px; + + .footer-card { + width: 100%; + background: var(--background-color-gray); + border-radius: 6px; + padding: 8px; + display: flex; + flex-direction: column; + gap: 6px; + + &:first-child { + width: 85%; + } + + .header { + font-size: var(--font-size-regular); + } + + .value-container { + display: flex; + flex-direction: row; + align-items: center; + justify-content: end; + gap: 6px; + } + } + + .shiftUtilization { + .value-container { + flex-direction: column; + align-items: flex-start; + justify-content: flex-start; + + .value { + font-size: var(--font-size-xlarge); + } + + .progress-wrapper { + width: 100%; + display: flex; + gap: 6px; + + .progress { + border-radius: 6px; + height: 5px; + + &:nth-child(1) { + background-color: #F3C64D; + } + + &:nth-child(2) { + background-color: #67B3F4; + } + + &:nth-child(3) { + background-color: #7981F5; + } + } + } + + .progress-indicator { + display: flex; + justify-content: space-between; + width: 100%; + gap: 6px; + + .shift-wrapper { + display: flex; + align-items: center; + gap: 5px; + + /* Align items vertically */ + &:nth-child(1) { + .indicator { + + background-color: #F3C64D; + } + } + + &:nth-child(2) { + .indicator { + + background-color: #67B3F4; + } + } + + &:nth-child(3) { + .indicator { + + background-color: #7981F5; + } + } + + label { + font-size: var(--font-size-small); + position: relative; + } + + .indicator { + display: inline-block; + width: 5px; + height: 5px; + border-radius: 50%; + + } + } + } + } + } + + } + + + } +} \ No newline at end of file diff --git a/app/src/styles/main.scss b/app/src/styles/main.scss index 5e46dd4..f0f7704 100644 --- a/app/src/styles/main.scss +++ b/app/src/styles/main.scss @@ -25,6 +25,8 @@ @use 'components/simulation/simulation'; @use 'components/menu/menu'; @use 'components/confirmationPopUp'; +@use 'components/analysis/analysis'; +@use 'components/analysis/ROISummary.scss'; // layout @use 'layout/loading';