react-pert 1.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Youcef Hammadi [@ucfx](https://github.com/ucfx)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,184 @@
1
+ <h1 align="center">pert-react</h1>
2
+
3
+ ## :star2: Overview
4
+
5
+ This package provides tools to calculate and visualize a PERT (Program Evaluation and Review Technique) diagram. It includes the following components and utilities:
6
+
7
+ - **`PertProvider`**: A context provider for managing and accessing PERT data.
8
+ - **`Pert`**: A component to render the PERT diagram (currently under development).
9
+ - **`usePertData`**: A custom hook to retrieve PERT results.
10
+
11
+ ### :rocket: Progress
12
+
13
+ - :white_check_mark: **PERT Calculator**: Fully functional.
14
+ - :construction: **PERT Diagram**: Currently under development.
15
+
16
+ ---
17
+
18
+ ## :clipboard: Installation
19
+
20
+ Install the package via npm:
21
+
22
+ ```bash
23
+ npm install react-pert
24
+ ```
25
+
26
+ ---
27
+
28
+ ## :book: Usage
29
+
30
+ ### Setup with `PertProvider`
31
+
32
+ Wrap your application or a specific part of it with `PertProvider` to provide context for managing PERT data:
33
+
34
+ > **Note**: The `Pert` component is under development and may not be fully functional yet.
35
+
36
+ ```jsx
37
+ import { Pert, PertProvider, type TaskType } from "react-pert";
38
+
39
+ const App = () => {
40
+ const tasks: TaskType[] = [
41
+ { key: "1", duration: 5, text: "A" },
42
+ { key: "2", duration: 4, text: "B" },
43
+ { key: "3", duration: 2, text: "C", dependsOn: ["1"] },
44
+ { key: "4", duration: 3, text: "D", dependsOn: ["2"] },
45
+ ...
46
+ ];
47
+
48
+ return (
49
+ <PertProvider>
50
+ <Pert tasks={tasks} /> /* PERT diagram currently under development */
51
+ <PertDetails />
52
+ </PertProvider>
53
+ );
54
+ };
55
+ ```
56
+
57
+ ---
58
+
59
+ ### Using `usePertData` Hook
60
+
61
+ The `usePertData` hook allows you to calculate and retrieve PERT results. Here's how you can use it:
62
+
63
+ ```jsx
64
+ import { usePertData } from "react-pert";
65
+
66
+ const PertDetails = () => {
67
+ const { tasks, criticalPaths } = usePertData();
68
+
69
+ return (
70
+ <div>
71
+ <h3>Project Duration : {tasks[tasks.length - 1]?.lateStart}</h3>
72
+ <h3>Critical Paths:</h3>
73
+ <div>
74
+ {criticalPaths.map((cp, index) => (
75
+ <div key={index}>
76
+ {cp.map((p, i) => (
77
+ <label key={i}>
78
+ <span>{p.text}</span>
79
+ {i !== cp.length - 1 && <span> {"→"}</span>}
80
+ </label>
81
+ ))}
82
+ </div>
83
+ ))}
84
+ </div>
85
+ </div>
86
+ );
87
+ };
88
+ ```
89
+
90
+ ---
91
+
92
+ ## :books: API Reference
93
+
94
+ ### `PertProps`
95
+
96
+ | Attribute | Type | Description |
97
+ | --------- | ------------------------- | --------------------------------------------------------------- |
98
+ | `tasks` | [`TaskType[]`](#TaskType) | Array of tasks to be used for the calculation and PERT diagram. |
99
+
100
+ ### `Pert`
101
+
102
+ A visual representation of the PERT diagram (currently in development).
103
+
104
+ ---
105
+
106
+ ### `usePertData`
107
+
108
+ #### Returns:
109
+
110
+ - **`PertDataType`**: Contains all PERT data including tasks, levels, links, and critical paths.
111
+
112
+ ### `PertDataType`
113
+
114
+ | Attribute | Type | Description |
115
+ | --------------- | ----------------------------------------- | ----------------------------------------------------------- |
116
+ | `tasks` | [`TaskResultType[]`](#TaskResultType) | Array of tasks with PERT calculation results. |
117
+ | `levels` | [`LevelType`](#LevelType) | Mapping of task keys to their respective levels. |
118
+ | `links` | [`LinkType[]`](#LinkType) | Array of links representing the dependencies between tasks. |
119
+ | `criticalPaths` | [`CriticalPathType[]`](#CriticalPathType) | Array of critical paths in the project. |
120
+
121
+ ### `TaskType`
122
+
123
+ | Attribute | Type | Description |
124
+ | ------------ | ---------- | --------------------------------------------------------------- |
125
+ | `key` | `string` | Unique identifier for the task. |
126
+ | `text` | `string` | Description or name of the task. |
127
+ | `duration` | `number` | Duration of the task in some unit (e.g., days). |
128
+ | `dependsOn?` | `string[]` | Array of task keys that the current task depends on (optional). |
129
+
130
+ ### `TaskResultType`
131
+
132
+ | Attribute | Type | Description |
133
+ | ------------- | --------- | -------------------------------------------- |
134
+ | `earlyStart` | `number` | The earliest start time for the task. |
135
+ | `earlyFinish` | `number` | The earliest finish time for the task. |
136
+ | `lateStart` | `number` | The latest start time for the task. |
137
+ | `lateFinish` | `number` | The latest finish time for the task. |
138
+ | `level` | `number` | The level of the task in the PERT diagram. |
139
+ | `critical` | `boolean` | Indicates if the task is on a critical path. |
140
+ | `freeFloat` | `number` | The free float time of the task. |
141
+ | `totalFloat` | `number` | The total float time of the task. |
142
+ | `index` | `number` | Index of the task in the sequence. |
143
+
144
+ ### `LevelType`
145
+
146
+ | Attribute | Type | Description |
147
+ | --------- | ---------- | ----------------------------------------- |
148
+ | `key` | `string` | The task key. |
149
+ | `value` | `string[]` | Array of task keys at the specific level. |
150
+
151
+ ### `LinkType`
152
+
153
+ | Attribute | Type | Description |
154
+ | ---------- | --------- | ------------------------------------------------- |
155
+ | `from` | `string` | The task key from which the link originates. |
156
+ | `to` | `string` | The task key to which the link leads. |
157
+ | `critical` | `boolean` | Indicates if the link is part of a critical path. |
158
+
159
+ ### `PathItemType`
160
+
161
+ | Attribute | Type | Description |
162
+ | --------- | -------- | -------------------------------------------------- |
163
+ | `text` | `string` | Text description of the task on the critical path. |
164
+ | `key` | `string` | The key of the task on the critical path. |
165
+
166
+ ### `CriticalPathType`
167
+
168
+ | | Type | Description |
169
+ | ------------------ | ---------------- | --------------------------------------------------------------------------------------------------------------- |
170
+ | `CriticalPathType` | `PathItemType[]` | An array of tasks (`PathItemType`) forming the critical path. Each element contains the task's `text` and `key` |
171
+
172
+ ---
173
+
174
+ ## :handshake: Contributing
175
+
176
+ We welcome contributions! If you encounter any bugs or have feature requests, please open an issue or submit a pull request.
177
+
178
+ ---
179
+
180
+ ## :page_with_curl: License
181
+
182
+ This package is open-source and licensed under the [MIT License](LICENSE.md).
183
+
184
+ Enjoy building with **PERT Diagram**! :relaxed:
@@ -0,0 +1,59 @@
1
+ import { JSX as JSX_2 } from 'react/jsx-runtime';
2
+ import { ReactNode } from 'react';
3
+
4
+ export declare type CriticalPathType = PathItemType[];
5
+
6
+ export declare type LevelType = {
7
+ [key: string]: string[];
8
+ };
9
+
10
+ export declare type LinkType = {
11
+ from: string;
12
+ to: string;
13
+ critical: boolean;
14
+ };
15
+
16
+ declare type PathItemType = {
17
+ text: string;
18
+ key: string;
19
+ };
20
+
21
+ export declare function Pert({ tasks }: PertProps): JSX_2.Element;
22
+
23
+ export declare type PertDataType = {
24
+ tasks: TaskResultType[];
25
+ levels: LevelType;
26
+ links: LinkType[];
27
+ criticalPaths: CriticalPathType[];
28
+ };
29
+
30
+ export declare type PertProps = {
31
+ tasks: TaskType[];
32
+ };
33
+
34
+ export declare const PertProvider: ({ children }: {
35
+ children: ReactNode;
36
+ }) => JSX_2.Element;
37
+
38
+ export declare type TaskResultType = TaskType & {
39
+ earlyStart: number;
40
+ earlyFinish: number;
41
+ lateStart: number;
42
+ lateFinish: number;
43
+ level: number;
44
+ critical: boolean;
45
+ freeFloat: number;
46
+ totalFloat: number;
47
+ index: number;
48
+ };
49
+
50
+ export declare type TaskType = {
51
+ key: string;
52
+ text: string;
53
+ duration: number;
54
+ dependsOn?: string[];
55
+ };
56
+
57
+ export declare const usePertData: () => PertDataType;
58
+
59
+ export { }
package/dist/index.js ADDED
@@ -0,0 +1,216 @@
1
+ var k = Object.defineProperty;
2
+ var f = (l, e, s) => e in l ? k(l, e, { enumerable: !0, configurable: !0, writable: !0, value: s }) : l[e] = s;
3
+ var n = (l, e, s) => f(l, typeof e != "symbol" ? e + "" : e, s);
4
+ import { jsx as p, Fragment as v } from "react/jsx-runtime";
5
+ import { createContext as P, useState as T, useContext as y, useEffect as u } from "react";
6
+ const d = () => Math.random().toString(36).substring(7);
7
+ let g = class {
8
+ constructor(e) {
9
+ n(this, "levels");
10
+ n(this, "tasksMap");
11
+ n(this, "initialData");
12
+ n(this, "links");
13
+ n(this, "criticalPaths");
14
+ n(this, "tasks");
15
+ n(this, "lastTaskKey");
16
+ n(this, "startTaskKey");
17
+ this.initialData = e, this.tasksMap = /* @__PURE__ */ new Map(), this.lastTaskKey = `Finish-${d()}`, this.startTaskKey = `Start-${d()}`, this.levels = {}, this.links = [], this.criticalPaths = [], this.tasks = [];
18
+ }
19
+ convertDataToMap() {
20
+ this.tasksMap = /* @__PURE__ */ new Map(), this.tasksMap.set(this.startTaskKey, {
21
+ key: this.startTaskKey,
22
+ duration: 0,
23
+ text: "Start"
24
+ }), this.initialData.forEach((e, s) => {
25
+ if (this.tasksMap.has(e.key)) throw Error(`Duplicate keys found ${e.key}`);
26
+ this.tasksMap.set(
27
+ e.key,
28
+ !e.dependsOn || e.dependsOn.length === 0 ? { ...e, dependsOn: [this.startTaskKey], index: s } : { ...e, index: s }
29
+ );
30
+ });
31
+ }
32
+ calculatePERT() {
33
+ this.convertDataToMap(), this.calcLevels();
34
+ let e = Object.keys(this.levels).map((t) => parseInt(t));
35
+ e.forEach((t) => {
36
+ this.levels[t].forEach((a) => {
37
+ this.calculateEarlyTimes(a);
38
+ });
39
+ });
40
+ let s = Array.from(this.tasksMap.values()).reduce(
41
+ (t, a) => t.earlyFinish > a.earlyFinish ? t : a
42
+ );
43
+ s.lateFinish = s.earlyFinish, s.critical = !0, this.tasksMap.set(this.lastTaskKey, {
44
+ key: this.lastTaskKey,
45
+ text: "Finish",
46
+ dependsOn: [s.key],
47
+ duration: 0,
48
+ earlyStart: s.earlyFinish,
49
+ earlyFinish: s.earlyFinish,
50
+ lateFinish: s.earlyFinish,
51
+ lateStart: s.earlyFinish,
52
+ level: e.length,
53
+ critical: !0,
54
+ freeFloat: 0,
55
+ totalFloat: 0,
56
+ index: this.tasksMap.size - 1
57
+ }), this.levels[e.length] = [this.lastTaskKey];
58
+ for (let t = e.length - 1; t >= 0; t--)
59
+ this.levels[e[t]].forEach((a) => {
60
+ this.calculateLateTimes(a, s.lateFinish);
61
+ });
62
+ e.forEach((t) => {
63
+ this.levels[t] = this.levels[t].sort(
64
+ (a, i) => this.tasksMap.get(a).index - this.tasksMap.get(i).index
65
+ );
66
+ }), this.tasks = Array.from(this.tasksMap.values());
67
+ }
68
+ getSuccessors(e) {
69
+ return Array.from(this.tasksMap.values()).filter(
70
+ (s) => s.dependsOn && s.dependsOn.includes(e.key)
71
+ );
72
+ }
73
+ calculateEarlyTimes(e) {
74
+ const s = this.tasksMap.get(e);
75
+ if (!s.dependsOn)
76
+ s.earlyStart = 0;
77
+ else {
78
+ let t = 0;
79
+ s.dependsOn.forEach((a) => {
80
+ const i = this.tasksMap.get(a);
81
+ t = Math.max(
82
+ t,
83
+ i.earlyStart + i.duration
84
+ );
85
+ }), s.earlyStart = t;
86
+ }
87
+ s.earlyFinish = s.earlyStart + s.duration;
88
+ }
89
+ calculateLateTimes(e, s) {
90
+ const t = this.tasksMap.get(e), a = this.getSuccessors(t);
91
+ let i = a.length === 0 ? s : Math.min(...a.map((r) => r.lateFinish - r.duration));
92
+ t.lateFinish = i, t.lateStart = t.lateFinish - t.duration, t.critical = t.earlyFinish === t.lateFinish, t.critical ? (t.freeFloat = 0, t.totalFloat = 0) : (t.freeFloat = (a.length === 0 ? s : Math.min(...a.map((r) => r.earlyStart))) - t.earlyFinish, t.totalFloat = t.lateFinish - t.earlyFinish);
93
+ }
94
+ calcLevels() {
95
+ this.levels = {};
96
+ let e = [];
97
+ const s = (t) => {
98
+ if (e.includes(t.key))
99
+ throw new Error("Circular dependency detected");
100
+ if (!t.dependsOn || t.dependsOn.length === 0)
101
+ t.level = 0;
102
+ else {
103
+ let a = 0;
104
+ t.dependsOn.forEach((i) => {
105
+ const r = this.tasksMap.get(i);
106
+ if (!r)
107
+ throw new Error(`Task with KEY '${i}' was not found.`);
108
+ r.level === void 0 && (e.push(t.key), s(r)), a = Math.max(a, r.level + 1);
109
+ }), t.level = a;
110
+ }
111
+ this.levels[t.level] || (this.levels[t.level] = []), this.levels[t.level].includes(t.key) || this.levels[t.level].push(t.key);
112
+ };
113
+ this.tasksMap.forEach((t) => {
114
+ e = [], s(t);
115
+ });
116
+ }
117
+ calcNodeLinks() {
118
+ const e = [], s = [];
119
+ this.tasksMap.forEach((t, a) => {
120
+ t.dependsOn ? t.dependsOn.forEach((i) => {
121
+ s.push(i), e.push({
122
+ from: i,
123
+ to: t.key,
124
+ critical: t.critical && this.tasksMap.get(i).critical
125
+ });
126
+ }) : a !== this.startTaskKey && e.push({
127
+ from: this.startTaskKey,
128
+ to: t.key,
129
+ critical: t.critical
130
+ });
131
+ }), this.tasksMap.forEach((t) => {
132
+ !s.includes(t.key) && t.key !== this.lastTaskKey && e.push({
133
+ from: t.key,
134
+ to: this.lastTaskKey,
135
+ critical: t.critical
136
+ });
137
+ }), this.links = e;
138
+ }
139
+ calcCriticalPaths() {
140
+ this.calcNodeLinks();
141
+ const e = [];
142
+ this.links.filter(
143
+ (t) => t.critical && t.from === this.startTaskKey && t.to !== this.lastTaskKey
144
+ ).forEach((t) => {
145
+ const a = [t], i = (r) => {
146
+ const o = this.links.filter(
147
+ (h) => h.critical && h.from === r.to && h.to !== this.lastTaskKey
148
+ );
149
+ o.length === 0 ? e.push(
150
+ a.map((h) => ({
151
+ text: this.tasksMap.get(h.to).text,
152
+ key: h.to
153
+ }))
154
+ ) : o.forEach((h) => {
155
+ a.push(h), i(h), a.pop();
156
+ });
157
+ };
158
+ i(t);
159
+ }), this.criticalPaths = e;
160
+ }
161
+ getTasks() {
162
+ return this.calculatePERT(), this.tasks;
163
+ }
164
+ getCriticalPaths() {
165
+ return this.criticalPaths.length === 0 && this.calcCriticalPaths(), this.criticalPaths;
166
+ }
167
+ getLevels() {
168
+ return this.levels;
169
+ }
170
+ getNodeLinks() {
171
+ return this.links;
172
+ }
173
+ solve() {
174
+ return {
175
+ tasks: this.getTasks(),
176
+ levels: this.getLevels(),
177
+ criticalPaths: this.getCriticalPaths(),
178
+ links: this.getNodeLinks()
179
+ };
180
+ }
181
+ };
182
+ const M = {
183
+ tasks: [],
184
+ levels: {},
185
+ links: [],
186
+ criticalPaths: []
187
+ }, c = P(null), w = ({ children: l }) => {
188
+ const [e, s] = T(M), t = (a) => {
189
+ const i = new g(a).solve();
190
+ s(i);
191
+ };
192
+ return /* @__PURE__ */ p(c.Provider, { value: { pertData: e, calculatePertResults: t }, children: l });
193
+ }, S = () => {
194
+ const l = y(c);
195
+ if (!l)
196
+ throw new Error("usePertData must be used within a PertProvider");
197
+ return l.pertData;
198
+ }, m = () => {
199
+ const l = y(c);
200
+ if (!l)
201
+ throw new Error("usePertContext must be used within a PertProvider");
202
+ return l;
203
+ };
204
+ function O({ tasks: l }) {
205
+ const { pertData: e, calculatePertResults: s } = m();
206
+ return u(() => {
207
+ s(l);
208
+ }, [l]), u(() => {
209
+ console.log("data", e);
210
+ }, [e]), /* @__PURE__ */ p(v, { children: "PertChart" });
211
+ }
212
+ export {
213
+ O as Pert,
214
+ w as PertProvider,
215
+ S as usePertData
216
+ };
@@ -0,0 +1 @@
1
+ (function(l,r){typeof exports=="object"&&typeof module<"u"?r(exports,require("react/jsx-runtime"),require("react")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react"],r):(l=typeof globalThis<"u"?globalThis:l||self,r(l["react-pert"]={},l["react/jsx-runtime"],l.React))})(this,function(l,r,n){"use strict";var g=Object.defineProperty;var m=(l,r,n)=>r in l?g(l,r,{enumerable:!0,configurable:!0,writable:!0,value:n}):l[r]=n;var u=(l,r,n)=>m(l,typeof r!="symbol"?r+"":r,n);const f=()=>Math.random().toString(36).substring(7);let y=class{constructor(e){u(this,"levels");u(this,"tasksMap");u(this,"initialData");u(this,"links");u(this,"criticalPaths");u(this,"tasks");u(this,"lastTaskKey");u(this,"startTaskKey");this.initialData=e,this.tasksMap=new Map,this.lastTaskKey=`Finish-${f()}`,this.startTaskKey=`Start-${f()}`,this.levels={},this.links=[],this.criticalPaths=[],this.tasks=[]}convertDataToMap(){this.tasksMap=new Map,this.tasksMap.set(this.startTaskKey,{key:this.startTaskKey,duration:0,text:"Start"}),this.initialData.forEach((e,s)=>{if(this.tasksMap.has(e.key))throw Error(`Duplicate keys found ${e.key}`);this.tasksMap.set(e.key,!e.dependsOn||e.dependsOn.length===0?{...e,dependsOn:[this.startTaskKey],index:s}:{...e,index:s})})}calculatePERT(){this.convertDataToMap(),this.calcLevels();let e=Object.keys(this.levels).map(t=>parseInt(t));e.forEach(t=>{this.levels[t].forEach(a=>{this.calculateEarlyTimes(a)})});let s=Array.from(this.tasksMap.values()).reduce((t,a)=>t.earlyFinish>a.earlyFinish?t:a);s.lateFinish=s.earlyFinish,s.critical=!0,this.tasksMap.set(this.lastTaskKey,{key:this.lastTaskKey,text:"Finish",dependsOn:[s.key],duration:0,earlyStart:s.earlyFinish,earlyFinish:s.earlyFinish,lateFinish:s.earlyFinish,lateStart:s.earlyFinish,level:e.length,critical:!0,freeFloat:0,totalFloat:0,index:this.tasksMap.size-1}),this.levels[e.length]=[this.lastTaskKey];for(let t=e.length-1;t>=0;t--)this.levels[e[t]].forEach(a=>{this.calculateLateTimes(a,s.lateFinish)});e.forEach(t=>{this.levels[t]=this.levels[t].sort((a,i)=>this.tasksMap.get(a).index-this.tasksMap.get(i).index)}),this.tasks=Array.from(this.tasksMap.values())}getSuccessors(e){return Array.from(this.tasksMap.values()).filter(s=>s.dependsOn&&s.dependsOn.includes(e.key))}calculateEarlyTimes(e){const s=this.tasksMap.get(e);if(!s.dependsOn)s.earlyStart=0;else{let t=0;s.dependsOn.forEach(a=>{const i=this.tasksMap.get(a);t=Math.max(t,i.earlyStart+i.duration)}),s.earlyStart=t}s.earlyFinish=s.earlyStart+s.duration}calculateLateTimes(e,s){const t=this.tasksMap.get(e),a=this.getSuccessors(t);let i=a.length===0?s:Math.min(...a.map(c=>c.lateFinish-c.duration));t.lateFinish=i,t.lateStart=t.lateFinish-t.duration,t.critical=t.earlyFinish===t.lateFinish,t.critical?(t.freeFloat=0,t.totalFloat=0):(t.freeFloat=(a.length===0?s:Math.min(...a.map(c=>c.earlyStart)))-t.earlyFinish,t.totalFloat=t.lateFinish-t.earlyFinish)}calcLevels(){this.levels={};let e=[];const s=t=>{if(e.includes(t.key))throw new Error("Circular dependency detected");if(!t.dependsOn||t.dependsOn.length===0)t.level=0;else{let a=0;t.dependsOn.forEach(i=>{const c=this.tasksMap.get(i);if(!c)throw new Error(`Task with KEY '${i}' was not found.`);c.level===void 0&&(e.push(t.key),s(c)),a=Math.max(a,c.level+1)}),t.level=a}this.levels[t.level]||(this.levels[t.level]=[]),this.levels[t.level].includes(t.key)||this.levels[t.level].push(t.key)};this.tasksMap.forEach(t=>{e=[],s(t)})}calcNodeLinks(){const e=[],s=[];this.tasksMap.forEach((t,a)=>{t.dependsOn?t.dependsOn.forEach(i=>{s.push(i),e.push({from:i,to:t.key,critical:t.critical&&this.tasksMap.get(i).critical})}):a!==this.startTaskKey&&e.push({from:this.startTaskKey,to:t.key,critical:t.critical})}),this.tasksMap.forEach(t=>{!s.includes(t.key)&&t.key!==this.lastTaskKey&&e.push({from:t.key,to:this.lastTaskKey,critical:t.critical})}),this.links=e}calcCriticalPaths(){this.calcNodeLinks();const e=[];this.links.filter(t=>t.critical&&t.from===this.startTaskKey&&t.to!==this.lastTaskKey).forEach(t=>{const a=[t],i=c=>{const p=this.links.filter(o=>o.critical&&o.from===c.to&&o.to!==this.lastTaskKey);p.length===0?e.push(a.map(o=>({text:this.tasksMap.get(o.to).text,key:o.to}))):p.forEach(o=>{a.push(o),i(o),a.pop()})};i(t)}),this.criticalPaths=e}getTasks(){return this.calculatePERT(),this.tasks}getCriticalPaths(){return this.criticalPaths.length===0&&this.calcCriticalPaths(),this.criticalPaths}getLevels(){return this.levels}getNodeLinks(){return this.links}solve(){return{tasks:this.getTasks(),levels:this.getLevels(),criticalPaths:this.getCriticalPaths(),links:this.getNodeLinks()}}};const k={tasks:[],levels:{},links:[],criticalPaths:[]},d=n.createContext(null),v=({children:h})=>{const[e,s]=n.useState(k),t=a=>{const i=new y(a).solve();s(i)};return r.jsx(d.Provider,{value:{pertData:e,calculatePertResults:t},children:h})},P=()=>{const h=n.useContext(d);if(!h)throw new Error("usePertData must be used within a PertProvider");return h.pertData},T=()=>{const h=n.useContext(d);if(!h)throw new Error("usePertContext must be used within a PertProvider");return h};function M({tasks:h}){const{pertData:e,calculatePertResults:s}=T();return n.useEffect(()=>{s(h)},[h]),n.useEffect(()=>{console.log("data",e)},[e]),r.jsx(r.Fragment,{children:"PertChart"})}l.Pert=M,l.PertProvider=v,l.usePertData=P,Object.defineProperty(l,Symbol.toStringTag,{value:"Module"})});
package/package.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "react-pert",
3
+ "version": "1.0.0-beta.1",
4
+ "type": "module",
5
+ "files": [
6
+ "dist",
7
+ "README.md",
8
+ "LICENSE.md"
9
+ ],
10
+ "main": "./dist/index.umd.cjs",
11
+ "module": "./dist/index.js",
12
+ "exports": {
13
+ ".": {
14
+ "import": "./dist/index.js",
15
+ "require": "./dist/index.umd.cjs"
16
+ }
17
+ },
18
+ "types": "./dist/index.d.ts",
19
+ "sideEffects": false,
20
+ "publishConfig": {
21
+ "access": "public"
22
+ },
23
+ "homepage": "https://github.com/ucfx/react-pert#readme",
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "https://github.com/ucfx/react-pert"
27
+ },
28
+ "author": {
29
+ "name": "Youcef Hammadi",
30
+ "email": "ucefhammadi@gmail.com"
31
+ },
32
+ "scripts": {
33
+ "dev": "vite",
34
+ "prebuild": "rimraf dist",
35
+ "build": "tsc -b && vite build",
36
+ "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
37
+ "semantic-release": "semantic-release"
38
+ },
39
+ "dependencies": {
40
+ "react": "^18.3.1",
41
+ "react-dom": "^18.3.1"
42
+ },
43
+ "devDependencies": {
44
+ "@eslint/js": "^9.11.1",
45
+ "@types/node": "^22.7.5",
46
+ "@types/react": "^18.3.10",
47
+ "@types/react-dom": "^18.3.0",
48
+ "@vitejs/plugin-react": "^4.3.2",
49
+ "eslint": "^9.11.1",
50
+ "eslint-plugin-react-hooks": "^5.1.0-rc.0",
51
+ "eslint-plugin-react-refresh": "^0.4.12",
52
+ "globals": "^15.9.0",
53
+ "rimraf": "^6.0.1",
54
+ "semantic-release": "^24.1.2",
55
+ "typescript": "^5.5.3",
56
+ "typescript-eslint": "^8.7.0",
57
+ "vite": "^5.4.8",
58
+ "vite-plugin-dts": "^4.2.4",
59
+ "vite-tsconfig-paths": "^5.0.1"
60
+ },
61
+ "keywords": [
62
+ "react",
63
+ "pert",
64
+ "pert chart",
65
+ "react-pert",
66
+ "react pert",
67
+ "graph",
68
+ "diagram",
69
+ "chart"
70
+ ]
71
+ }