├── .eslintignore
├── .eslintrc.json
├── .gitignore
├── .prettierignore
├── .prettierrc
├── .vscode
├── extensions.json
└── settings.json
├── README.md
├── components
├── GanttChart
│ ├── AddButton.js
│ ├── AddTask.js
│ ├── AddTaskDuration.js
│ ├── GanttChart.js
│ ├── Grid.js
│ ├── Settings.js
│ ├── Tasks.js
│ ├── TimeRange.js
│ └── TimeTable.js
└── Layout.js
├── constants.js
├── helpers
└── dateFunctions.js
├── next.config.js
├── package-lock.json
├── package.json
├── pages
├── _app.js
├── _document.js
└── index.js
├── public
├── data.json
├── data
│ └── gantt-data.json
└── favicon.ico
├── styles
└── global.js
└── utils
└── fetchWrapper.js
/.eslintignore:
--------------------------------------------------------------------------------
1 | .next
2 | next-env.d.ts
3 | node_modules
4 | yarn.lock
5 | package-lock.json
6 | public
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["next/core-web-vitals", "prettier"]
3 | }
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 | .pnpm-debug.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 | .idea
34 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | .next
2 | next-env.d.ts
3 | node_modules
4 | yarn.lock
5 | package-lock.json
6 | public
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "endOfLine": "lf",
3 | "printWidth": 80,
4 | "tabWidth": 2,
5 | "trailingComma": "es5",
6 | "singleQuote": true
7 | }
8 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"]
3 | }
4 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "workbench.colorCustomizations": {
3 | "titleBar.activeForeground": "#000",
4 | "titleBar.inactiveForeground": "#000",
5 | "titleBar.activeBackground": "#4dbef6",
6 | "titleBar.inactiveBackground": "#4dbef6"
7 | },
8 | "editor.formatOnPaste": true,
9 | "editor.formatOnSave": true,
10 | "editor.defaultFormatter": "esbenp.prettier-vscode",
11 | "editor.codeActionsOnSave": {
12 | "source.fixAll.eslint": true,
13 | "source.fixAll.format": true
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Tutorial: Creating a Gantt chart with React using Next.js
2 |
3 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
4 |
5 | ## Getting Started
6 |
7 | - Install dependencies:
8 |
9 | ```bash
10 | npm install
11 | ```
12 |
13 | - To run the development server:
14 |
15 | ```bash
16 | npm run dev
17 | ```
18 |
19 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
20 |
21 | You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file.
22 |
23 | [API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/createResponse.js`.
24 |
25 | The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
26 |
27 | ## Deploy on Vercel
28 |
29 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
30 |
31 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
32 |
33 | ## Resources
34 |
35 | To learn more about Next.js, take a look at the following resources:
36 |
37 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
38 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
39 |
40 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
41 |
--------------------------------------------------------------------------------
/components/GanttChart/AddButton.js:
--------------------------------------------------------------------------------
1 | export default function AddButton() {
2 | return (
3 |
25 | );
26 | }
27 |
--------------------------------------------------------------------------------
/components/GanttChart/AddTask.js:
--------------------------------------------------------------------------------
1 | import { useState } from 'react';
2 | import AddButton from './AddButton';
3 |
4 | export default function AddTask() {
5 | const [task, setTask] = useState('');
6 |
7 | function onChange(e) {
8 | setTask(e.target.value);
9 | }
10 |
11 | function handleSubmit(e) {
12 | e.preventDefault();
13 | setTask('');
14 | }
15 |
16 | return (
17 |
36 | );
37 | }
38 |
--------------------------------------------------------------------------------
/components/GanttChart/AddTaskDuration.js:
--------------------------------------------------------------------------------
1 | import { useState } from 'react';
2 | import AddButton from './AddButton';
3 |
4 | export default function AddTaskDuration() {
5 | const [task, setTask] = useState('');
6 | const [startDate, setStartDate] = useState('2022-01-01');
7 | const [endDate, setEndDate] = useState('2022-01-03');
8 |
9 | function onChange(e) {
10 | const { value, id } = e.target;
11 |
12 | if (id === 'select-task') {
13 | setTask(value);
14 | }
15 | if (id === 'start-date') {
16 | setStartDate(value);
17 | }
18 | if (id === 'end-date') {
19 | setEndDate(value);
20 | }
21 | }
22 |
23 | function handleSubmit(e) {
24 | e.preventDefault();
25 | }
26 |
27 | return (
28 |
101 | );
102 | }
103 |
--------------------------------------------------------------------------------
/components/GanttChart/GanttChart.js:
--------------------------------------------------------------------------------
1 | export default function GanttChart() {
2 | return (
3 |
4 |
18 |
19 | );
20 | }
21 |
--------------------------------------------------------------------------------
/components/GanttChart/Grid.js:
--------------------------------------------------------------------------------
1 | export default function Grid({ children }) {
2 | return (
3 |
4 | {children}
5 |
14 |
15 | );
16 | }
17 |
--------------------------------------------------------------------------------
/components/GanttChart/Settings.js:
--------------------------------------------------------------------------------
1 | export default function Settings({ children }) {
2 | return (
3 |
4 | {children}
5 |
12 |
13 | );
14 | }
15 |
--------------------------------------------------------------------------------
/components/GanttChart/Tasks.js:
--------------------------------------------------------------------------------
1 | import { useEffect, useRef } from 'react';
2 |
3 | export default function Tasks() {
4 | return (
5 |
6 |
7 |
8 |
9 |
10 |
44 |
45 | );
46 | }
47 |
--------------------------------------------------------------------------------
/components/GanttChart/TimeRange.js:
--------------------------------------------------------------------------------
1 | import { months } from '../../constants';
2 |
3 | export default function TimeRange() {
4 | // add date selector values
5 | let monthsOptions = [];
6 | for (let i = 0; i < months.length; i++) {
7 | monthsOptions.push(
8 |
11 | );
12 | }
13 |
14 | const yearsOptions = [];
15 | for (let i = 2022; i <= 2050; i++) {
16 | yearsOptions.push(
17 |
20 | );
21 | }
22 |
23 | function onChange(e) {
24 | const { value, id } = e.target;
25 | }
26 |
27 | return (
28 |
29 |
Tracker Period
30 |
31 |
51 |
52 |
72 |
73 |
107 |
108 | );
109 | }
110 |
--------------------------------------------------------------------------------
/components/GanttChart/TimeTable.js:
--------------------------------------------------------------------------------
1 | import { useState } from 'react';
2 | import { monthDiff } from '../../helpers/dateFunctions';
3 |
4 | export default function TimeTable() {
5 | // for dynamic css styling
6 | const ganttTimePeriod = {
7 | display: 'grid',
8 | gridAutoFlow: 'column',
9 | gridAutoColumns: 'minmax(30px, 1fr)',
10 | outline: '0.5px solid var(--color-outline)',
11 | textAlign: 'center',
12 | height: 'var(--cell-height)',
13 | };
14 |
15 | const ganttTimePeriodSpan = {
16 | margin: 'auto',
17 | };
18 |
19 | const ganttTimePeriodCell = {
20 | position: 'relative',
21 | outline: '0.5px solid var(--color-outline)',
22 | marginTop: '0.5px',
23 | };
24 |
25 | const taskDuration = {
26 | position: 'absolute',
27 | height: 'calc(var(--cell-height) - 1px)',
28 | zIndex: '1',
29 | background:
30 | 'linear-gradient(90deg, var(--color-primary-light) 0%, var(--color-primary-dark) 100%)',
31 | borderRadius: 'var(--border-radius)',
32 | boxShadow: '3px 3px 3px rgba(0, 0, 0, 0.05)',
33 | cursor: 'move',
34 | };
35 |
36 | // creating rows
37 | const startMonth = new Date(
38 | parseInt(timeRange.fromSelectYear),
39 | timeRange.fromSelectMonth
40 | );
41 | const endMonth = new Date(
42 | parseInt(timeRange.toSelectYear),
43 | timeRange.toSelectMonth
44 | );
45 | const numMonths = monthDiff(startMonth, endMonth) + 1;
46 | let month = new Date(startMonth);
47 |
48 | let monthRows = [];
49 | let dayRows = [];
50 | let dayRow = [];
51 | let weekRows = [];
52 | let weekRow = [];
53 | let taskRows = [];
54 | let taskRow = [];
55 |
56 | return (
57 |
61 | {monthRows}
62 | {dayRows}
63 | {weekRows}
64 |
73 | {taskRows}
74 |
75 |
105 |
106 | );
107 | }
108 |
--------------------------------------------------------------------------------
/components/Layout.js:
--------------------------------------------------------------------------------
1 | import globalStyles from '../styles/global.js';
2 |
3 | function Layout(props) {
4 | return (
5 |
6 | {props.children}
7 |
10 |
11 | );
12 | }
13 |
14 | export default Layout;
15 |
--------------------------------------------------------------------------------
/constants.js:
--------------------------------------------------------------------------------
1 | export const months = [
2 | 'Jan',
3 | 'Feb',
4 | 'Mar',
5 | 'Apr',
6 | 'May',
7 | 'Jun',
8 | 'Jul',
9 | 'Aug',
10 | 'Sep',
11 | 'Oct',
12 | 'Nov',
13 | 'Dec',
14 | ];
15 |
--------------------------------------------------------------------------------
/helpers/dateFunctions.js:
--------------------------------------------------------------------------------
1 | export function monthDiff(firstMonth, lastMonth) {
2 | let months;
3 | months = (lastMonth.getFullYear() - firstMonth.getFullYear()) * 12;
4 | months -= firstMonth.getMonth();
5 | months += lastMonth.getMonth();
6 | return months <= 0 ? 0 : months;
7 | }
8 |
9 | export function dayDiff(startDate, endDate) {
10 | const difference =
11 | new Date(endDate).getTime() - new Date(startDate).getTime();
12 | const days = Math.ceil(difference / (1000 * 3600 * 24)) + 1;
13 | return days;
14 | }
15 |
16 | export function getDaysInMonth(year, month) {
17 | return new Date(year, month, 0).getDate();
18 | }
19 |
20 | export function getDayOfWeek(year, month, day) {
21 | const daysOfTheWeekArr = ['M', 'T', 'W', 'T', 'F', 'S', 'S'];
22 | const dayOfTheWeekIndex = new Date(year, month, day).getDay();
23 | return daysOfTheWeekArr[dayOfTheWeekIndex];
24 | }
25 |
26 | export function createFormattedDateFromStr(year, month, day) {
27 | let monthStr = month.toString();
28 | let dayStr = day.toString();
29 |
30 | if (monthStr.length === 1) {
31 | monthStr = `0${monthStr}`;
32 | }
33 | if (dayStr.length === 1) {
34 | dayStr = `0${dayStr}`;
35 | }
36 | return `${year}-${monthStr}-${dayStr}`;
37 | }
38 |
39 | export function createFormattedDateFromDate(date) {
40 | let monthStr = (date.getMonth() + 1).toString();
41 | let dayStr = date.getDate().toString();
42 |
43 | if (monthStr.length === 1) {
44 | monthStr = `0${monthStr}`;
45 | }
46 | if (dayStr.length === 1) {
47 | dayStr = `0${dayStr}`;
48 | }
49 | return `${date.getFullYear()}-${monthStr}-${dayStr}`;
50 | }
51 |
--------------------------------------------------------------------------------
/next.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | reactStrictMode: true,
4 | swcMinify: true,
5 | }
6 |
7 | module.exports = nextConfig
8 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "web",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "next": "12.2.3",
13 | "react": "18.2.0",
14 | "react-dom": "18.2.0"
15 | },
16 | "devDependencies": {
17 | "eslint": "8.20.0",
18 | "eslint-config-next": "12.2.3",
19 | "eslint-config-prettier": "^8.5.0",
20 | "prettier": "^2.7.1"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/pages/_app.js:
--------------------------------------------------------------------------------
1 | import Layout from '../components/Layout';
2 |
3 | function MyApp({ Component, pageProps }) {
4 | return (
5 |
6 |
7 |
8 | );
9 | }
10 |
11 | export default MyApp;
12 |
--------------------------------------------------------------------------------
/pages/_document.js:
--------------------------------------------------------------------------------
1 | import Document, { Html, Head, Main, NextScript } from 'next/document';
2 |
3 | class MyDocument extends Document {
4 | render() {
5 | return (
6 |
7 |
8 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | );
19 | }
20 | }
21 |
22 | export default MyDocument;
23 |
--------------------------------------------------------------------------------
/pages/index.js:
--------------------------------------------------------------------------------
1 | import Head from 'next/head';
2 | import GanttChart from '../components/GanttChart/GanttChart';
3 |
4 | export default function Home() {
5 | return (
6 |
7 |
8 |
Creating a Gantt chart with React using Next.js
9 |
10 |
11 |
12 |
13 |
14 | Gantt Tracker
15 |
16 |
17 |
31 |
32 | );
33 | }
34 |
--------------------------------------------------------------------------------
/public/data.json:
--------------------------------------------------------------------------------
1 | {
2 | "tasks": [
3 | {
4 | "id": 1,
5 | "name": "Task 1"
6 | },
7 | {
8 | "id": 2,
9 | "name": "Task 2"
10 | },
11 | {
12 | "id": 3,
13 | "name": "Task 3"
14 | },
15 | {
16 | "id": 4,
17 | "name": "Task 4"
18 | },
19 | {
20 | "id": 5,
21 | "name": "Task 5"
22 | },
23 | {
24 | "id": 6,
25 | "name": "Task 6"
26 | },
27 | {
28 | "id": 7,
29 | "name": "Task 7"
30 | },
31 | {
32 | "id": 8,
33 | "name": "Task 8"
34 | }
35 | ],
36 | "taskDurations": [
37 | {
38 | "id": 1,
39 | "start": "2022-01-02",
40 | "end": "2022-01-8",
41 | "task": 1
42 | },
43 | {
44 | "id": 2,
45 | "start": "2022-01-10",
46 | "end": "2022-01-15",
47 | "task": 2
48 | },
49 | {
50 | "id": 3,
51 | "start": "2022-01-11",
52 | "end": "2022-01-18",
53 | "task": 4
54 | }
55 | ]
56 | }
--------------------------------------------------------------------------------
/public/data/gantt-data.json:
--------------------------------------------------------------------------------
1 | {
2 | "success": true,
3 | "project": {
4 | "calendar": "general",
5 | "startDate": "2022-03-14",
6 | "hoursPerDay": 24,
7 | "daysPerWeek": 5,
8 | "daysPerMonth": 20
9 | },
10 | "calendars": {
11 | "rows": [
12 | {
13 | "id": "general",
14 | "name": "General",
15 | "intervals": [
16 | {
17 | "recurrentStartDate": "on Sat at 0:00",
18 | "recurrentEndDate": "on Mon at 0:00",
19 | "isWorking": false
20 | }
21 | ],
22 | "expanded": true,
23 | "children": [
24 | {
25 | "id": "business",
26 | "name": "Business",
27 | "intervals": [
28 | {
29 | "recurrentStartDate": "every weekday at 12:00",
30 | "recurrentEndDate": "every weekday at 13:00",
31 | "isWorking": false
32 | },
33 | {
34 | "recurrentStartDate": "every weekday at 17:00",
35 | "recurrentEndDate": "every weekday at 08:00",
36 | "isWorking": false
37 | }
38 | ]
39 | },
40 | {
41 | "id": "night",
42 | "name": "Night shift",
43 | "intervals": [
44 | {
45 | "recurrentStartDate": "every weekday at 6:00",
46 | "recurrentEndDate": "every weekday at 22:00",
47 | "isWorking": false
48 | }
49 | ]
50 | }
51 | ]
52 | }
53 | ]
54 | },
55 | "tasks": {
56 | "rows": [
57 | {
58 | "id": 1000,
59 | "name": "Launch SaaS Product",
60 | "percentDone": 50,
61 | "startDate": "2022-03-14",
62 | "expanded": true,
63 | "children": [
64 | {
65 | "id": 1,
66 | "name": "Setup web server",
67 | "percentDone": 50,
68 | "duration": 10,
69 | "startDate": "2022-03-14",
70 | "rollup": true,
71 | "endDate": "2022-03-23",
72 | "expanded": true,
73 | "children": [
74 | {
75 | "id": 11,
76 | "name": "Install Apache",
77 | "percentDone": 50,
78 | "startDate": "2022-03-14",
79 | "rollup": true,
80 | "duration": 3,
81 | "color": "teal",
82 | "endDate": "2022-03-17",
83 | "cost": 200,
84 | "baselines": [
85 | {
86 | "startDate": "2022-03-13T23:00:00",
87 | "endDate": "2022-03-16T23:00:00"
88 | },
89 | {
90 | "startDate": "2022-03-13T23:00:00",
91 | "endDate": "2022-03-16T23:00:00"
92 | },
93 | {
94 | "startDate": "2022-03-13T23:00:00",
95 | "endDate": "2022-03-16T23:00:00"
96 | }
97 | ]
98 | },
99 | {
100 | "id": 12,
101 | "name": "Configure firewall",
102 | "percentDone": 50,
103 | "startDate": "2022-03-14",
104 | "rollup": true,
105 | "duration": 3,
106 | "endDate": "2022-03-17",
107 | "showInTimeline": true,
108 | "cost": 1000,
109 | "baselines": [
110 | {
111 | "startDate": "2022-03-13T23:00:00",
112 | "endDate": "2022-03-16T23:00:00"
113 | },
114 | {
115 | "startDate": "2022-03-13T23:00:00",
116 | "endDate": "2022-03-16T23:00:00"
117 | },
118 | {
119 | "startDate": "2022-03-13T23:00:00",
120 | "endDate": "2022-03-16T23:00:00"
121 | }
122 | ]
123 | },
124 | {
125 | "id": 13,
126 | "name": "Setup load balancer",
127 | "percentDone": 50,
128 | "startDate": "2022-03-14",
129 | "rollup": true,
130 | "duration": 3,
131 | "endDate": "2022-03-17",
132 | "cost": 1200,
133 | "baselines": [
134 | {
135 | "startDate": "2022-03-13T23:00:00",
136 | "endDate": "2022-03-16T23:00:00"
137 | },
138 | {
139 | "startDate": "2022-03-13T23:00:00",
140 | "endDate": "2022-03-16T23:00:00"
141 | },
142 | {
143 | "startDate": "2022-03-13T23:00:00",
144 | "endDate": "2022-03-16T23:00:00"
145 | }
146 | ]
147 | },
148 | {
149 | "id": 14,
150 | "name": "Configure ports",
151 | "percentDone": 50,
152 | "startDate": "2022-03-14",
153 | "rollup": true,
154 | "duration": 2,
155 | "endDate": "2022-03-16",
156 | "cost": 750,
157 | "baselines": [
158 | {
159 | "startDate": "2022-03-13T23:00:00",
160 | "endDate": "2022-03-15T23:00:00"
161 | },
162 | {
163 | "startDate": "2022-03-13T23:00:00",
164 | "endDate": "2022-03-15T23:00:00"
165 | },
166 | {
167 | "startDate": "2022-03-13T23:00:00",
168 | "endDate": "2022-03-15T23:00:00"
169 | }
170 | ]
171 | },
172 | {
173 | "id": 15,
174 | "name": "Run tests",
175 | "percentDone": 0,
176 | "startDate": "2022-03-21",
177 | "rollup": true,
178 | "duration": 2,
179 | "endDate": "2022-03-23",
180 | "cost": 5000,
181 | "baselines": [
182 | {
183 | "startDate": "2022-03-20T23:00:00",
184 | "endDate": "2022-03-22T23:00:00"
185 | },
186 | {
187 | "startDate": "2022-03-20T23:00:00",
188 | "endDate": "2022-03-22T23:00:00"
189 | },
190 | {
191 | "startDate": "2022-03-20T23:00:00",
192 | "endDate": "2022-03-22T23:00:00"
193 | }
194 | ]
195 | }
196 | ],
197 | "baselines": [
198 | {
199 | "startDate": "2022-03-13T23:00:00",
200 | "endDate": "2022-03-22T23:00:00"
201 | },
202 | {
203 | "startDate": "2022-03-13T23:00:00",
204 | "endDate": "2022-03-22T23:00:00"
205 | },
206 | {
207 | "startDate": "2022-03-13T23:00:00",
208 | "endDate": "2022-03-22T23:00:00"
209 | }
210 | ]
211 | },
212 | {
213 | "id": 2,
214 | "name": "Website Design",
215 | "percentDone": 60,
216 | "startDate": "2022-03-23",
217 | "rollup": true,
218 | "endDate": "2022-04-13",
219 | "expanded": true,
220 | "children": [
221 | {
222 | "id": 21,
223 | "name": "Contact designers",
224 | "percentDone": 70,
225 | "startDate": "2022-03-23",
226 | "rollup": true,
227 | "duration": 5,
228 | "endDate": "2022-03-30",
229 | "cost": 500,
230 | "baselines": [
231 | {
232 | "startDate": "2022-03-22T23:00:00",
233 | "endDate": "2022-03-25T23:00:00"
234 | },
235 | {
236 | "startDate": "2022-03-22T23:00:00",
237 | "endDate": "2022-03-28T23:00:00"
238 | },
239 | {
240 | "startDate": "2022-03-22T23:00:00",
241 | "endDate": "2022-03-29T23:00:00"
242 | }
243 | ]
244 | },
245 | {
246 | "id": 22,
247 | "name": "Create shortlist of three designers",
248 | "percentDone": 60,
249 | "startDate": "2022-03-30",
250 | "rollup": true,
251 | "duration": 1,
252 | "endDate": "2022-03-31",
253 | "cost": 1000,
254 | "baselines": [
255 | {
256 | "startDate": "2022-03-27T23:00:00",
257 | "endDate": "2022-03-28T23:00:00"
258 | },
259 | {
260 | "startDate": "2022-03-28T23:00:00",
261 | "endDate": "2022-03-29T23:00:00"
262 | },
263 | {
264 | "startDate": "2022-03-29T23:00:00",
265 | "endDate": "2022-03-30T23:00:00"
266 | }
267 | ]
268 | },
269 | {
270 | "id": 23,
271 | "name": "Select & review final design",
272 | "percentDone": 50,
273 | "startDate": "2022-03-31",
274 | "rollup": true,
275 | "duration": 2,
276 | "showInTimeline": true,
277 | "endDate": "2022-04-02",
278 | "cost": 1000,
279 | "baselines": [
280 | {
281 | "startDate": "2022-03-28T23:00:00",
282 | "endDate": "2022-03-30T23:00:00"
283 | },
284 | {
285 | "startDate": "2022-03-29T23:00:00",
286 | "endDate": "2022-03-31T23:00:00"
287 | },
288 | {
289 | "startDate": "2022-03-30T23:00:00",
290 | "endDate": "2022-04-01T23:00:00"
291 | }
292 | ]
293 | },
294 | {
295 | "id": 24,
296 | "name": "Inform management about decision",
297 | "percentDone": 100,
298 | "startDate": "2022-04-04",
299 | "rollup": true,
300 | "duration": 0,
301 | "cost": 500,
302 | "baselines": [
303 | {
304 | "startDate": "2022-03-30T23:00:00",
305 | "endDate": "2022-03-30T23:00:00"
306 | },
307 | {
308 | "startDate": "2022-03-31T23:00:00",
309 | "endDate": "2022-03-31T23:00:00"
310 | },
311 | {
312 | "startDate": "2022-04-01T23:00:00",
313 | "endDate": "2022-04-01T23:00:00"
314 | }
315 | ]
316 | },
317 | {
318 | "id": 25,
319 | "name": "Apply design to web site",
320 | "percentDone": 0,
321 | "startDate": "2022-04-04",
322 | "rollup": true,
323 | "duration": 7,
324 | "endDate": "2022-04-13",
325 | "cost": 11000,
326 | "baselines": [
327 | {
328 | "startDate": "2022-03-30T23:00:00",
329 | "endDate": "2022-04-08T23:00:00"
330 | },
331 | {
332 | "startDate": "2022-03-31T23:00:00",
333 | "endDate": "2022-04-11T23:00:00"
334 | },
335 | {
336 | "startDate": "2022-04-03T23:00:00",
337 | "endDate": "2022-04-12T23:00:00"
338 | }
339 | ]
340 | }
341 | ],
342 | "baselines": [
343 | {
344 | "startDate": "2022-03-22T23:00:00",
345 | "endDate": "2022-04-08T23:00:00"
346 | },
347 | {
348 | "startDate": "2022-03-22T23:00:00",
349 | "endDate": "2022-04-11T23:00:00"
350 | },
351 | {
352 | "startDate": "2022-03-22T23:00:00",
353 | "endDate": "2022-04-12T23:00:00"
354 | }
355 | ]
356 | },
357 | {
358 | "id": 3,
359 | "name": "Setup Test Strategy",
360 | "percentDone": 20,
361 | "startDate": "2022-03-14",
362 | "expanded": true,
363 | "children": [
364 | {
365 | "id": 31,
366 | "name": "Hire QA staff",
367 | "percentDone": 40,
368 | "startDate": "2022-03-14",
369 | "duration": 5,
370 | "endDate": "2022-03-19",
371 | "cost": 6000,
372 | "baselines": [
373 | {
374 | "startDate": "2022-03-13T23:00:00",
375 | "endDate": "2022-03-18T23:00:00"
376 | },
377 | {
378 | "startDate": "2022-03-13T23:00:00",
379 | "endDate": "2022-03-18T23:00:00"
380 | },
381 | {
382 | "startDate": "2022-03-13T23:00:00",
383 | "endDate": "2022-03-18T23:00:00"
384 | }
385 | ]
386 | },
387 | {
388 | "id": 33,
389 | "name": "Write test specs",
390 | "percentDone": 9,
391 | "duration": 5,
392 | "startDate": "2022-03-21",
393 | "expanded": true,
394 | "children": [
395 | {
396 | "id": 331,
397 | "name": "Unit tests",
398 | "percentDone": 20,
399 | "startDate": "2022-03-21",
400 | "duration": 10,
401 | "endDate": "2022-04-02",
402 | "showInTimeline": true,
403 | "cost": 7000,
404 | "baselines": [
405 | {
406 | "startDate": "2022-03-20T23:00:00",
407 | "endDate": "2022-04-01T23:00:00"
408 | },
409 | {
410 | "startDate": "2022-03-20T23:00:00",
411 | "endDate": "2022-04-01T23:00:00"
412 | },
413 | {
414 | "startDate": "2022-03-20T23:00:00",
415 | "endDate": "2022-04-01T23:00:00"
416 | }
417 | ]
418 | },
419 | {
420 | "id": 332,
421 | "name": "UI unit tests / individual screens",
422 | "percentDone": 10,
423 | "startDate": "2022-03-21",
424 | "duration": 5,
425 | "endDate": "2022-03-26",
426 | "showInTimeline": true,
427 | "cost": 5000,
428 | "baselines": [
429 | {
430 | "startDate": "2022-03-20T23:00:00",
431 | "endDate": "2022-03-25T23:00:00"
432 | },
433 | {
434 | "startDate": "2022-03-20T23:00:00",
435 | "endDate": "2022-03-25T23:00:00"
436 | },
437 | {
438 | "startDate": "2022-03-20T23:00:00",
439 | "endDate": "2022-03-25T23:00:00"
440 | }
441 | ]
442 | },
443 | {
444 | "id": 333,
445 | "name": "Application tests",
446 | "percentDone": 0,
447 | "startDate": "2022-03-21",
448 | "duration": 10,
449 | "endDate": "2022-04-02",
450 | "cost": 2500,
451 | "baselines": [
452 | {
453 | "startDate": "2022-03-20T23:00:00",
454 | "endDate": "2022-04-01T23:00:00"
455 | },
456 | {
457 | "startDate": "2022-03-20T23:00:00",
458 | "endDate": "2022-04-01T23:00:00"
459 | },
460 | {
461 | "startDate": "2022-03-20T23:00:00",
462 | "endDate": "2022-04-01T23:00:00"
463 | }
464 | ]
465 | },
466 | {
467 | "id": 334,
468 | "name": "Monkey tests",
469 | "percentDone": 0,
470 | "startDate": "2022-03-21",
471 | "duration": 1,
472 | "endDate": "2022-03-22",
473 | "cost": 250,
474 | "baselines": [
475 | {
476 | "startDate": "2022-03-20T23:00:00",
477 | "endDate": "2022-03-21T23:00:00"
478 | },
479 | {
480 | "startDate": "2022-03-20T23:00:00",
481 | "endDate": "2022-03-21T23:00:00"
482 | },
483 | {
484 | "startDate": "2022-03-20T23:00:00",
485 | "endDate": "2022-03-21T23:00:00"
486 | }
487 | ]
488 | }
489 | ],
490 | "endDate": "2022-04-02",
491 | "baselines": [
492 | {
493 | "startDate": "2022-03-20T23:00:00",
494 | "endDate": "2022-04-01T23:00:00"
495 | },
496 | {
497 | "startDate": "2022-03-20T23:00:00",
498 | "endDate": "2022-04-01T23:00:00"
499 | },
500 | {
501 | "startDate": "2022-03-20T23:00:00",
502 | "endDate": "2022-04-01T23:00:00"
503 | }
504 | ]
505 | }
506 | ],
507 | "endDate": "2022-04-02",
508 | "baselines": [
509 | {
510 | "startDate": "2022-03-13T23:00:00",
511 | "endDate": "2022-04-01T23:00:00"
512 | },
513 | {
514 | "startDate": "2022-03-13T23:00:00",
515 | "endDate": "2022-04-01T23:00:00"
516 | },
517 | {
518 | "startDate": "2022-03-13T23:00:00",
519 | "endDate": "2022-04-01T23:00:00"
520 | }
521 | ]
522 | },
523 | {
524 | "id": 4,
525 | "name": "Application Implementation",
526 | "percentDone": 60,
527 | "startDate": "2022-04-04",
528 | "expanded": true,
529 | "children": [
530 | {
531 | "id": 400,
532 | "name": "Phase #1",
533 | "expanded": true,
534 | "children": [
535 | {
536 | "id": 41,
537 | "name": "Authentication module",
538 | "percentDone": 100,
539 | "duration": 5,
540 | "startDate": "2022-04-04",
541 | "endDate": "2022-04-09",
542 | "cost": 8000,
543 | "baselines": [
544 | {
545 | "startDate": "2022-04-03T23:00:00",
546 | "endDate": "2022-04-08T23:00:00"
547 | },
548 | {
549 | "startDate": "2022-04-03T23:00:00",
550 | "endDate": "2022-04-08T23:00:00"
551 | },
552 | {
553 | "startDate": "2022-04-03T23:00:00",
554 | "endDate": "2022-04-08T23:00:00"
555 | }
556 | ]
557 | },
558 | {
559 | "id": 42,
560 | "name": "Single sign on",
561 | "percentDone": 100,
562 | "duration": 3,
563 | "startDate": "2022-04-04",
564 | "endDate": "2022-04-07",
565 | "cost": 4700,
566 | "baselines": [
567 | {
568 | "startDate": "2022-04-03T23:00:00",
569 | "endDate": "2022-04-06T23:00:00"
570 | },
571 | {
572 | "startDate": "2022-04-03T23:00:00",
573 | "endDate": "2022-04-06T23:00:00"
574 | },
575 | {
576 | "startDate": "2022-04-03T23:00:00",
577 | "endDate": "2022-04-06T23:00:00"
578 | }
579 | ]
580 | },
581 | {
582 | "id": 43,
583 | "name": "Implement role based access",
584 | "percentDone": 0,
585 | "duration": 4,
586 | "startDate": "2022-04-04",
587 | "endDate": "2022-04-08",
588 | "cost": 5800,
589 | "baselines": [
590 | {
591 | "startDate": "2022-04-03T23:00:00",
592 | "endDate": "2022-04-07T23:00:00"
593 | },
594 | {
595 | "startDate": "2022-04-03T23:00:00",
596 | "endDate": "2022-04-07T23:00:00"
597 | },
598 | {
599 | "startDate": "2022-04-03T23:00:00",
600 | "endDate": "2022-04-07T23:00:00"
601 | }
602 | ]
603 | },
604 | {
605 | "id": 44,
606 | "name": "Basic test coverage",
607 | "showInTimeline": true,
608 | "percentDone": 0,
609 | "duration": 3,
610 | "startDate": "2022-04-04",
611 | "endDate": "2022-04-07",
612 | "cost": 7000,
613 | "baselines": [
614 | {
615 | "startDate": "2022-04-03T23:00:00",
616 | "endDate": "2022-04-06T23:00:00"
617 | },
618 | {
619 | "startDate": "2022-04-03T23:00:00",
620 | "endDate": "2022-04-06T23:00:00"
621 | },
622 | {
623 | "startDate": "2022-04-03T23:00:00",
624 | "endDate": "2022-04-06T23:00:00"
625 | }
626 | ]
627 | },
628 | {
629 | "id": 45,
630 | "name": "Verify high test coverage",
631 | "percentDone": 0,
632 | "duration": 2,
633 | "startDate": "2022-04-11",
634 | "endDate": "2022-04-13",
635 | "cost": 16000,
636 | "baselines": [
637 | {
638 | "startDate": "2022-04-11",
639 | "endDate": "2022-04-13"
640 | },
641 | {
642 | "startDate": "2022-04-11",
643 | "endDate": "2022-04-13"
644 | },
645 | {
646 | "startDate": "2022-04-11",
647 | "endDate": "2022-04-13"
648 | }
649 | ]
650 | },
651 | {
652 | "id": 46,
653 | "name": "Make backup",
654 | "percentDone": 0,
655 | "duration": 0,
656 | "startDate": "2022-04-13",
657 | "endDate": "2022-04-13",
658 | "showInTimeline": true,
659 | "rollup": true,
660 | "cost": 500,
661 | "baselines": [
662 | {
663 | "startDate": "2022-04-11",
664 | "endDate": "2022-04-11"
665 | },
666 | {
667 | "startDate": "2022-04-12",
668 | "endDate": "2022-04-12"
669 | },
670 | {
671 | "startDate": "2022-04-13",
672 | "endDate": "2022-04-13"
673 | }
674 | ]
675 | }
676 | ],
677 | "startDate": "2022-04-04",
678 | "endDate": "2022-04-09",
679 | "baselines": [
680 | {
681 | "startDate": "2022-04-03T23:00:00",
682 | "endDate": "2022-04-08T23:00:00"
683 | },
684 | {
685 | "startDate": "2022-04-03T23:00:00",
686 | "endDate": "2022-04-08T23:00:00"
687 | },
688 | {
689 | "startDate": "2022-04-03T23:00:00",
690 | "endDate": "2022-04-08T23:00:00"
691 | }
692 | ]
693 | },
694 | {
695 | "id": 401,
696 | "name": "Phase #2",
697 | "expanded": true,
698 | "children": [
699 | {
700 | "id": 4011,
701 | "name": "Authentication module",
702 | "percentDone": 70,
703 | "duration": 15,
704 | "startDate": "2022-04-11",
705 | "endDate": "2022-05-02",
706 | "cost": 1200,
707 | "baselines": [
708 | {
709 | "startDate": "2022-04-10T23:00:00",
710 | "endDate": "2022-05-01T23:00:00"
711 | },
712 | {
713 | "startDate": "2022-04-10T23:00:00",
714 | "endDate": "2022-05-01T23:00:00"
715 | },
716 | {
717 | "startDate": "2022-04-10T23:00:00",
718 | "endDate": "2022-05-01T23:00:00"
719 | }
720 | ]
721 | },
722 | {
723 | "id": 4012,
724 | "name": "Single sign on",
725 | "percentDone": 60,
726 | "duration": 5,
727 | "startDate": "2022-04-11",
728 | "endDate": "2022-04-16",
729 | "cost": 2500,
730 | "baselines": [
731 | {
732 | "startDate": "2022-04-10T23:00:00",
733 | "endDate": "2022-04-15T23:00:00"
734 | },
735 | {
736 | "startDate": "2022-04-10T23:00:00",
737 | "endDate": "2022-04-15T23:00:00"
738 | },
739 | {
740 | "startDate": "2022-04-10T23:00:00",
741 | "endDate": "2022-04-15T23:00:00"
742 | }
743 | ]
744 | },
745 | {
746 | "id": 4013,
747 | "name": "Implement role based access",
748 | "percentDone": 50,
749 | "duration": 21,
750 | "startDate": "2022-04-11",
751 | "endDate": "2022-05-12",
752 | "cost": 4100,
753 | "baselines": [
754 | {
755 | "startDate": "2022-04-10T23:00:00",
756 | "endDate": "2022-05-11T23:00:00"
757 | },
758 | {
759 | "startDate": "2022-04-10T23:00:00",
760 | "endDate": "2022-05-11T23:00:00"
761 | },
762 | {
763 | "startDate": "2022-04-10T23:00:00",
764 | "endDate": "2022-05-11T23:00:00"
765 | }
766 | ]
767 | },
768 | {
769 | "id": 4014,
770 | "name": "Basic test coverage",
771 | "percentDone": 0,
772 | "duration": 20,
773 | "startDate": "2022-04-11",
774 | "endDate": "2022-05-09",
775 | "cost": 1100,
776 | "baselines": [
777 | {
778 | "startDate": "2022-04-10T23:00:00",
779 | "endDate": "2022-05-08T23:00:00"
780 | },
781 | {
782 | "startDate": "2022-04-10T23:00:00",
783 | "endDate": "2022-05-08T23:00:00"
784 | },
785 | {
786 | "startDate": "2022-04-10T23:00:00",
787 | "endDate": "2022-05-08T23:00:00"
788 | }
789 | ]
790 | },
791 | {
792 | "id": 4015,
793 | "name": "Verify high test coverage",
794 | "percentDone": 0,
795 | "duration": 4,
796 | "startDate": "2022-04-11",
797 | "endDate": "2022-04-15",
798 | "cost": 3000,
799 | "baselines": [
800 | {
801 | "startDate": "2022-04-10T23:00:00",
802 | "endDate": "2022-04-14T23:00:00"
803 | },
804 | {
805 | "startDate": "2022-04-10T23:00:00",
806 | "endDate": "2022-04-14T23:00:00"
807 | },
808 | {
809 | "startDate": "2022-04-10T23:00:00",
810 | "endDate": "2022-04-14T23:00:00"
811 | }
812 | ]
813 | }
814 | ],
815 | "startDate": "2022-04-11",
816 | "endDate": "2022-05-12",
817 | "baselines": [
818 | {
819 | "startDate": "2022-04-10T23:00:00",
820 | "endDate": "2022-05-11T23:00:00"
821 | },
822 | {
823 | "startDate": "2022-04-10T23:00:00",
824 | "endDate": "2022-05-11T23:00:00"
825 | },
826 | {
827 | "startDate": "2022-04-10T23:00:00",
828 | "endDate": "2022-05-11T23:00:00"
829 | }
830 | ]
831 | },
832 | {
833 | "id": 402,
834 | "name": "Acceptance phase",
835 | "expanded": true,
836 | "children": [
837 | {
838 | "id": 4031,
839 | "name": "Company bug bash",
840 | "percentDone": 70,
841 | "duration": 3,
842 | "startDate": "2022-05-12",
843 | "endDate": "2022-05-15",
844 | "cost": 10000,
845 | "baselines": [
846 | {
847 | "startDate": "2022-05-11T23:00:00",
848 | "endDate": "2022-05-14T23:00:00"
849 | },
850 | {
851 | "startDate": "2022-05-11T23:00:00",
852 | "endDate": "2022-05-14T23:00:00"
853 | },
854 | {
855 | "startDate": "2022-05-11T23:00:00",
856 | "endDate": "2022-05-14T23:00:00"
857 | }
858 | ]
859 | },
860 | {
861 | "id": 4032,
862 | "name": "Test all web pages",
863 | "percentDone": 60,
864 | "duration": 2,
865 | "startDate": "2022-05-12",
866 | "endDate": "2022-05-14",
867 | "cost": 5000,
868 | "baselines": [
869 | {
870 | "startDate": "2022-05-11T23:00:00",
871 | "endDate": "2022-05-13T23:00:00"
872 | },
873 | {
874 | "startDate": "2022-05-11T23:00:00",
875 | "endDate": "2022-05-13T23:00:00"
876 | },
877 | {
878 | "startDate": "2022-05-11T23:00:00",
879 | "endDate": "2022-05-13T23:00:00"
880 | }
881 | ]
882 | },
883 | {
884 | "id": 4033,
885 | "name": "Verify no broken links",
886 | "percentDone": 50,
887 | "duration": 4,
888 | "startDate": "2022-05-12",
889 | "endDate": "2022-05-16",
890 | "cost": 1000,
891 | "baselines": [
892 | {
893 | "startDate": "2022-05-11T23:00:00",
894 | "endDate": "2022-05-15T23:00:00"
895 | },
896 | {
897 | "startDate": "2022-05-11T23:00:00",
898 | "endDate": "2022-05-15T23:00:00"
899 | },
900 | {
901 | "startDate": "2022-05-11T23:00:00",
902 | "endDate": "2022-05-15T23:00:00"
903 | }
904 | ]
905 | },
906 | {
907 | "id": 4034,
908 | "name": "Make test release",
909 | "percentDone": 0,
910 | "duration": 3,
911 | "startDate": "2022-05-12",
912 | "endDate": "2022-05-15",
913 | "cost": 1200,
914 | "baselines": [
915 | {
916 | "startDate": "2022-05-11T23:00:00",
917 | "endDate": "2022-05-14T23:00:00"
918 | },
919 | {
920 | "startDate": "2022-05-11T23:00:00",
921 | "endDate": "2022-05-14T23:00:00"
922 | },
923 | {
924 | "startDate": "2022-05-11T23:00:00",
925 | "endDate": "2022-05-14T23:00:00"
926 | }
927 | ]
928 | },
929 | {
930 | "id": 4035,
931 | "name": "Send invitation email",
932 | "percentDone": 0,
933 | "duration": 0,
934 | "startDate": "2022-05-15",
935 | "endDate": "2022-05-16",
936 | "cost": 250,
937 | "baselines": [
938 | {
939 | "startDate": "2022-05-14T23:00:00",
940 | "endDate": "2022-05-14T23:00:00"
941 | },
942 | {
943 | "startDate": "2022-05-13T00:00:00",
944 | "endDate": "2022-05-13T00:00:00"
945 | },
946 | {
947 | "startDate": "2022-05-12T00:00:00",
948 | "endDate": "2022-05-12T00:00:00"
949 | }
950 | ]
951 | },
952 | {
953 | "id": 4036,
954 | "name": "Celebrate launch",
955 | "iconCls": "b-fa b-fa-glass-cheers",
956 | "percentDone": 0,
957 | "duration": 1,
958 | "startDate": "2022-05-12",
959 | "endDate": "2022-05-13",
960 | "cost": 2500,
961 | "baselines": [
962 | {
963 | "startDate": "2022-05-11T23:00:00",
964 | "endDate": "2022-05-12T23:00:00"
965 | },
966 | {
967 | "startDate": "2022-05-11T23:00:00",
968 | "endDate": "2022-05-12T23:00:00"
969 | },
970 | {
971 | "startDate": "2022-05-11T23:00:00",
972 | "endDate": "2022-05-12T23:00:00"
973 | }
974 | ]
975 | }
976 | ],
977 | "startDate": "2022-05-12",
978 | "endDate": "2022-05-16",
979 | "baselines": [
980 | {
981 | "startDate": "2022-05-11T23:00:00",
982 | "endDate": "2022-05-15T23:00:00"
983 | },
984 | {
985 | "startDate": "2022-05-11T23:00:00",
986 | "endDate": "2022-05-15T23:00:00"
987 | },
988 | {
989 | "startDate": "2022-05-11T23:00:00",
990 | "endDate": "2022-05-15T23:00:00"
991 | }
992 | ]
993 | }
994 | ],
995 | "endDate": "2022-05-16",
996 | "baselines": [
997 | {
998 | "startDate": "2022-04-03T23:00:00",
999 | "endDate": "2022-05-15T23:00:00"
1000 | },
1001 | {
1002 | "startDate": "2022-04-03T23:00:00",
1003 | "endDate": "2022-05-15T23:00:00"
1004 | },
1005 | {
1006 | "startDate": "2022-04-03T23:00:00",
1007 | "endDate": "2022-05-15T23:00:00"
1008 | }
1009 | ]
1010 | }
1011 | ],
1012 | "endDate": "2022-05-16",
1013 | "baselines": [
1014 | {
1015 | "startDate": "2022-03-13T23:00:00",
1016 | "endDate": "2022-05-15T23:00:00"
1017 | },
1018 | {
1019 | "startDate": "2022-03-13T23:00:00",
1020 | "endDate": "2022-05-15T23:00:00"
1021 | },
1022 | {
1023 | "startDate": "2022-03-13T23:00:00",
1024 | "endDate": "2022-05-15T23:00:00"
1025 | }
1026 | ]
1027 | }
1028 | ]
1029 | },
1030 | "dependencies": {
1031 | "rows": [
1032 | {
1033 | "id": 1,
1034 | "fromTask": 11,
1035 | "toTask": 15,
1036 | "lag": 2
1037 | },
1038 | {
1039 | "id": 2,
1040 | "fromTask": 12,
1041 | "toTask": 15
1042 | },
1043 | {
1044 | "id": 3,
1045 | "fromTask": 13,
1046 | "toTask": 15
1047 | },
1048 | {
1049 | "id": 4,
1050 | "fromTask": 14,
1051 | "toTask": 15
1052 | },
1053 | {
1054 | "id": 5,
1055 | "fromTask": 15,
1056 | "toTask": 21
1057 | },
1058 | {
1059 | "id": 7,
1060 | "fromTask": 21,
1061 | "toTask": 22
1062 | },
1063 | {
1064 | "id": 8,
1065 | "fromTask": 22,
1066 | "toTask": 23
1067 | },
1068 | {
1069 | "id": 9,
1070 | "fromTask": 23,
1071 | "toTask": 24
1072 | },
1073 | {
1074 | "id": 10,
1075 | "fromTask": 24,
1076 | "toTask": 25
1077 | },
1078 | {
1079 | "id": 11,
1080 | "fromTask": 31,
1081 | "toTask": 331
1082 | },
1083 | {
1084 | "id": 111,
1085 | "fromTask": 31,
1086 | "toTask": 332
1087 | },
1088 | {
1089 | "id": 112,
1090 | "fromTask": 31,
1091 | "toTask": 333
1092 | },
1093 | {
1094 | "id": 113,
1095 | "fromTask": 31,
1096 | "toTask": 334
1097 | },
1098 | {
1099 | "id": 12,
1100 | "fromTask": 400,
1101 | "toTask": 401
1102 | },
1103 | {
1104 | "id": 13,
1105 | "fromTask": 401,
1106 | "toTask": 402
1107 | },
1108 | {
1109 | "id": 15,
1110 | "fromTask": 3,
1111 | "toTask": 4
1112 | },
1113 | {
1114 | "id": 16,
1115 | "fromTask": 41,
1116 | "toTask": 45
1117 | },
1118 | {
1119 | "id": 17,
1120 | "fromTask": 42,
1121 | "toTask": 45
1122 | },
1123 | {
1124 | "id": 18,
1125 | "fromTask": 43,
1126 | "toTask": 45
1127 | },
1128 | {
1129 | "id": 19,
1130 | "fromTask": 44,
1131 | "toTask": 45
1132 | },
1133 | {
1134 | "id": 20,
1135 | "fromTask": 4034,
1136 | "toTask": 4035
1137 | }
1138 | ]
1139 | },
1140 | "resources": {
1141 | "rows": [
1142 | {
1143 | "id": 1,
1144 | "name": "Celia",
1145 | "city": "Barcelona",
1146 | "calendar": null,
1147 | "image": "celia.jpg"
1148 | },
1149 | {
1150 | "id": 2,
1151 | "name": "Lee",
1152 | "city": "London",
1153 | "calendar": null,
1154 | "image": "lee.jpg"
1155 | },
1156 | {
1157 | "id": 3,
1158 | "name": "Macy",
1159 | "city": "New York",
1160 | "calendar": null,
1161 | "image": "macy.jpg"
1162 | },
1163 | {
1164 | "id": 4,
1165 | "name": "Madison",
1166 | "city": "Barcelona",
1167 | "calendar": null,
1168 | "image": "madison.jpg"
1169 | },
1170 | {
1171 | "id": 5,
1172 | "name": "Rob",
1173 | "city": "Rome",
1174 | "calendar": "business",
1175 | "image": "rob.jpg"
1176 | },
1177 | {
1178 | "id": 6,
1179 | "name": "Dave",
1180 | "city": "Barcelona",
1181 | "calendar": "night",
1182 | "image": "dave.jpg"
1183 | },
1184 | {
1185 | "id": 7,
1186 | "name": "Dan",
1187 | "city": "London",
1188 | "calendar": "night",
1189 | "image": "dan.jpg"
1190 | },
1191 | {
1192 | "id": 8,
1193 | "name": "George",
1194 | "city": "New York",
1195 | "calendar": null,
1196 | "image": "george.jpg"
1197 | },
1198 | {
1199 | "id": 9,
1200 | "name": "Gloria",
1201 | "city": "Rome",
1202 | "calendar": null,
1203 | "image": "gloria.jpg"
1204 | },
1205 | {
1206 | "id": 10,
1207 | "name": "Henrik",
1208 | "city": "London",
1209 | "calendar": null,
1210 | "image": "henrik.jpg"
1211 | }
1212 | ]
1213 | },
1214 | "assignments": {
1215 | "rows": [
1216 | {
1217 | "id": 1,
1218 | "event": 11,
1219 | "resource": 1
1220 | },
1221 | {
1222 | "id": 2,
1223 | "event": 4033,
1224 | "resource": 1
1225 | },
1226 | {
1227 | "id": 3,
1228 | "event": 12,
1229 | "resource": 9
1230 | },
1231 | {
1232 | "id": 4,
1233 | "event": 13,
1234 | "resource": 2
1235 | },
1236 | {
1237 | "id": 5,
1238 | "event": 13,
1239 | "resource": 3
1240 | },
1241 | {
1242 | "id": 6,
1243 | "event": 13,
1244 | "resource": 6
1245 | },
1246 | {
1247 | "id": 7,
1248 | "event": 13,
1249 | "resource": 7
1250 | },
1251 | {
1252 | "id": 8,
1253 | "event": 13,
1254 | "resource": 8
1255 | },
1256 | {
1257 | "id": 9,
1258 | "event": 21,
1259 | "resource": 5
1260 | },
1261 | {
1262 | "id": 10,
1263 | "event": 21,
1264 | "resource": 9
1265 | },
1266 | {
1267 | "id": 11,
1268 | "event": 22,
1269 | "resource": 8
1270 | },
1271 | {
1272 | "id": 12,
1273 | "event": 25,
1274 | "resource": 3
1275 | }
1276 | ]
1277 | },
1278 | "timeRanges": {
1279 | "rows": [
1280 | {
1281 | "id": 1,
1282 | "name": "Important date",
1283 | "startDate": "2022-03-30",
1284 | "duration": 0,
1285 | "durationUnit": "d",
1286 | "cls": "b-fa b-fa-diamond"
1287 | }
1288 | ]
1289 | }
1290 | }
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bryntum/gantt-chart-nextjs-starter/1144d7b2a52027369c9e9b592810899d61fa9f44/public/favicon.ico
--------------------------------------------------------------------------------
/styles/global.js:
--------------------------------------------------------------------------------
1 | import css from 'styled-jsx/css';
2 |
3 | export default css.global`
4 | * {
5 | box-sizing: border-box;
6 | margin: 0;
7 | }
8 |
9 | html,
10 | body {
11 | font-family: 'Montserrat', sans-serif;
12 | color: #000;
13 | }
14 |
15 | input,
16 | select {
17 | font-family: 'Montserrat', sans-serif;
18 | font-size: 13px;
19 | padding: 5px 7px;
20 | margin: 1rem 0;
21 | display: inline-block;
22 | border: 1px solid #ccc;
23 | border-radius: 4px;
24 | }
25 |
26 | button {
27 | cursor: pointer;
28 | font-family: 'Montserrat', sans-serif;
29 | font-size: 13px;
30 | }
31 |
32 | h2 {
33 | font-size: 1.5rem;
34 | }
35 |
36 | form {
37 | padding: 1rem;
38 | }
39 |
40 | form > * {
41 | display: flex;
42 | align-items: center;
43 | }
44 | `;
45 |
--------------------------------------------------------------------------------
/utils/fetchWrapper.js:
--------------------------------------------------------------------------------
1 | // from https://kentcdodds.com/blog/replace-axios-with-a-simple-custom-fetch-wrapper
2 | export function client(endpoint, { body, ...customConfig } = {}) {
3 | const headers = { 'Content-Type': 'application/json' };
4 | const config = {
5 | method: body ? 'POST' : 'GET',
6 | ...customConfig,
7 | headers: {
8 | ...headers,
9 | ...customConfig.headers,
10 | },
11 | };
12 | if (body) {
13 | config.body = JSON.stringify(body);
14 | }
15 |
16 | return window.fetch(`${endpoint}`, config).then(async (response) => {
17 | if (response.ok) {
18 | return await response.json();
19 | } else {
20 | const errorMessage = await response.text();
21 | return Promise.reject(new Error(errorMessage));
22 | }
23 | });
24 | }
25 |
--------------------------------------------------------------------------------