From 75bdecb6291a4121786734e53a6b52c271b50774 Mon Sep 17 00:00:00 2001 From: Thibault Fouchet Date: Wed, 3 Jan 2024 18:39:41 +0100 Subject: [PATCH] pie_chart and line_chart update --- pwa/app/components/bar-chart/chart.jsx | 90 +++++++++ pwa/app/components/bar-chart/form.jsx | 41 +++++ pwa/app/components/donut.jsx | 94 ---------- pwa/app/components/donut.tsx | 242 +++++++++++++++++++++++++ pwa/app/components/line_chart.tsx | 65 +++++++ pwa/app/page.jsx | 68 +++++-- pwa/app/style/style.css | 43 +++++ pwa/config.js | 5 + pwa/next-env.d.ts | 5 + pwa/package-lock.json | 62 +++++++ pwa/package.json | 5 + pwa/tsconfig.json | 34 ++++ 12 files changed, 641 insertions(+), 113 deletions(-) create mode 100644 pwa/app/components/bar-chart/chart.jsx create mode 100644 pwa/app/components/bar-chart/form.jsx delete mode 100644 pwa/app/components/donut.jsx create mode 100644 pwa/app/components/donut.tsx create mode 100644 pwa/app/components/line_chart.tsx create mode 100644 pwa/app/style/style.css create mode 100644 pwa/config.js create mode 100644 pwa/next-env.d.ts create mode 100644 pwa/tsconfig.json diff --git a/pwa/app/components/bar-chart/chart.jsx b/pwa/app/components/bar-chart/chart.jsx new file mode 100644 index 0000000..9028d21 --- /dev/null +++ b/pwa/app/components/bar-chart/chart.jsx @@ -0,0 +1,90 @@ +import React, { useEffect, useRef } from 'react'; +import * as d3 from 'd3'; + +const BarChart = ({ data }) => { + const d3Chart = useRef(); + + const drawChart = () => { + const colorScale = d3.scaleOrdinal(d3.schemeCategory10); + const margin = { top: 50, right: 40, bottom: 70, left: 60 }; + const barWidth = 40; + const maxWidth = window.innerWidth - margin.left - margin.right; + let chartWidth = data.length * barWidth; + + if (chartWidth > maxWidth) { + chartWidth = maxWidth; + } + + const height = 500 - margin.top - margin.bottom; + + d3.select(d3Chart.current).select("svg").remove(); + + const svg = d3.select(d3Chart.current) + .append("svg") + .attr("width", chartWidth + margin.left + margin.right) + .attr("height", height + margin.top + margin.bottom) + .append("g") + .attr("transform", `translate(${margin.left}, ${margin.top})`); + + const x = d3.scaleBand() + .range([0, chartWidth]) + .padding(0.1); + const y = d3.scaleLinear() + .range([height, 0]); + + x.domain(data.map(d => d.date)); + y.domain([0, d3.max(data, d => d.occurrences)]); + + svg.selectAll(".bar") + .data(data) + .enter().append("rect") + .attr("class", "bar") + .attr("fill", d => colorScale(d.date)) + .attr("width", x.bandwidth()) + .attr("x", d => x(d.date)) + .attr("y", d => y(d.occurrences)) + .attr("height", d => height - y(d.occurrences)); + + // Axe X + svg.append("g") + .attr("transform", `translate(0, ${height})`) + .call(d3.axisBottom(x)) + .selectAll("text") + .attr("transform", "rotate(-45)") + .style("text-anchor", "end"); + + // Axe Y + svg.append("g") + .call(d3.axisLeft(y)); + + // Titre Axe X + svg.append("text") + .attr("x", chartWidth / 2) + .attr("y", height + margin.bottom - 20) + .style("text-anchor", "middle") + .text("Date"); + + // Titre Axe Y + svg.append("text") + .attr("transform", "rotate(-90)") + .attr("y", 0) + .attr("x", 0 - (height / 2)) + .attr("dy", "-1em") + .style("text-anchor", "middle") + .text("Nombre de ventes"); + }; + + useEffect(() => { + if (data && data.length > 0) { + drawChart(); + } + }, [data]); + + return ( +
+
+
+ ); +}; + +export default BarChart; diff --git a/pwa/app/components/bar-chart/form.jsx b/pwa/app/components/bar-chart/form.jsx new file mode 100644 index 0000000..610f403 --- /dev/null +++ b/pwa/app/components/bar-chart/form.jsx @@ -0,0 +1,41 @@ +import React, { useState } from 'react'; +import '../../style/style.css'; + +const DataForm = ({ onSubmit }) => { + const [startDate, setStartDate] = useState(''); + const [endDate, setEndDate] = useState(''); + const [granularity, setGranularity] = useState('month'); + + const handleSubmit = (event) => { + event.preventDefault(); + onSubmit({ startDate, endDate, granularity }); + }; + + return ( +
+ setStartDate(e.target.value)} + required + /> + setEndDate(e.target.value)} + required + /> + + +
+ ); +}; + +export default DataForm; diff --git a/pwa/app/components/donut.jsx b/pwa/app/components/donut.jsx deleted file mode 100644 index 34d6a9e..0000000 --- a/pwa/app/components/donut.jsx +++ /dev/null @@ -1,94 +0,0 @@ -"use client"; - -import React, {useEffect, useRef} from "react"; -import * as d3 from "d3"; - -const defaultColorScheme = [ - "#1e64af", "#419bd2", "#69beeb", "#d7b428", "#e19196", - "#a5236e", "#eb1e4b", "#f06937", "#f5dc50", "#2d969b", - "#335c6c", "#ff7d2d", "#fac846", "#a0c382", "#5f9b8c", - "#325a69", "#fff5af", "#e1a03c", "#a0282d", "#550a0f" -]; - -/** - * @param {object} props - * @param {[{id: string, count: number}]} props.data - */ -export default function Donut({data}) { - const ref = useRef(); - const width = 250; - const height = 250; - const padding = 20; - const thicknessOnMouseOver = 10; - const radius = Math.min(width, height) / 2 - padding; - const deathAngle = 0.2; - - useEffect(() => { - const rawPieData = d3.pie().value((x) => x.count)(data); - - let pieData = []; - const aggregate = { - data: { - id: "aggregate", - count: 0 - }, - index: rawPieData.length, - value: 0, - padAngle: 0, - startAngle: 0, - endAngle: Math.PI * 2 - }; - - let lastEndAngle = 0; - for (const d of rawPieData) { - if (d.endAngle > lastEndAngle) { - lastEndAngle = d.endAngle; - } - // if d angle is less than deathAngle, we don't show it in final donut - if (d.endAngle - d.startAngle < deathAngle) { - aggregate.startAngle = lastEndAngle; - aggregate.endAngle -= d.endAngle - d.startAngle; - aggregate.value += d.value; - aggregate.data.count += d.data.count; - } else { - pieData.push(d); - } - } - pieData.push(aggregate); - - const arc = d3 - .arc() - .innerRadius(radius * 0.6) - .outerRadius(radius) - - d3.select(ref.current) - .attr("width", width) - .attr("height", height) - .append("g") - .attr("transform", `translate(${width/2}, ${height/2})`) - .selectAll("path") - .data(pieData) - .enter() - .append("path") - .attr("d", arc) - .attr("fill", (x, i) => x.data.id === "aggregate" ? "#dddddd" : defaultColorScheme[i % data.length]) - .on("mouseover", function() { - d3.select(this) - .transition() - .duration(200) - .attr("stroke", this.attributes.fill.nodeValue) - .attr("stroke-width", thicknessOnMouseOver); - this.parentNode.appendChild(this); - }) - .on("mouseout", function() { - d3.select(this) - .transition() - .attr("stroke", "none") - .attr("stroke-width", 0); - }); - }, [data]); - - return ( - - ); -} diff --git a/pwa/app/components/donut.tsx b/pwa/app/components/donut.tsx new file mode 100644 index 0000000..2c76d5a --- /dev/null +++ b/pwa/app/components/donut.tsx @@ -0,0 +1,242 @@ +import React, { useState, useEffect, useRef } from "react"; +import * as d3 from "d3"; + +interface ChartData { + departement: string; + value: number; +} + +const Pie_Chart = () => { + const originalData: ChartData[] = [ + { departement: "01", value: 21050 }, + { departement: "02", value: 45954 }, + { departement: "03", value: 21584 }, + { departement: "04", value: 53486 }, + { departement: "05", value: 87965 }, + { departement: "06", value: 34864 }, + { departement: "07", value: 48453 }, + { departement: "08", value: 87652 }, + { departement: "09", value: 12346 }, + { departement: "10", value: 78654 }, + { departement: "11", value: 48453 }, + { departement: "12", value: 78654 }, + { departement: "13", value: 28203 }, + { departement: "14", value: 21050 }, + { departement: "15", value: 45211 }, + { departement: "16", value: 87945 }, + { departement: "17", value: 47853 }, + { departement: "18", value: 75213 }, + { departement: "19", value: 21050 }, + { departement: "20", value: 78542 }, + { departement: "21", value: 21050 }, + { departement: "22", value: 78432 }, + { departement: "23", value: 21050 }, + { departement: "24", value: 21050 }, + { departement: "25", value: 27420 }, + { departement: "26", value: 27420 }, + { departement: "27", value: 27420 }, + { departement: "28", value: 27420 }, + { departement: "29", value: 75423 }, + { departement: "30", value: 21050 }, + { departement: "31", value: 78543 }, + { departement: "32", value: 21050 }, + { departement: "33", value: 100 }, + { departement: "34", value: 21050 }, + { departement: "35", value: 21050 }, + { departement: "36", value: 500 }, + { departement: "37", value: 24128 }, + { departement: "38", value: 24128 }, + { departement: "39", value: 2400 }, + { departement: "40", value: 24128 }, + { departement: "41", value: 7856 }, + { departement: "42", value: 2014 }, + { departement: "43", value: 3500 }, + { departement: "44", value: 24128 }, + { departement: "45", value: 2035 }, + { departement: "46", value: 150 }, + { departement: "47", value: 980 }, + { departement: "48", value: 748 }, + { departement: "49", value: 38806 }, + { departement: "50", value: 38806 }, + { departement: "51", value: 78543 }, + { departement: "52", value: 12999 }, + { departement: "53", value: 7865 }, + { departement: "54", value: 12999 }, + { departement: "55", value: 7854 }, + { departement: "56", value: 3698 }, + { departement: "57", value: 7412 }, + { departement: "58", value: 1236 }, + { departement: "59", value: 4789 }, + { departement: "60", value: 8963 }, + { departement: "61", value: 6321 }, + { departement: "62", value: 2145 }, + { departement: "63", value: 3256 }, + { departement: "64", value: 6589 }, + { departement: "65", value: 8965 }, + { departement: "66", value: 4587 }, + { departement: "67", value: 5697 }, + { departement: "68", value: 5413 }, + { departement: "69", value: 9713 }, + { departement: "70", value: 5418 }, + { departement: "71", value: 12999 }, + { departement: "72", value: 8642 }, + { departement: "73", value: 12999 }, + { departement: "74", value: 7319 }, + { departement: "75", value: 8246 }, + { departement: "76", value: 7853 }, + { departement: "77", value: 4687 }, + { departement: "78", value: 2480 }, + { departement: "79", value: 4283 }, + { departement: "80", value: 7352 }, + { departement: "81", value: 2052 }, + { departement: "82", value: 4052 }, + { departement: "83", value: 3058 }, + { departement: "84", value: 5602 }, + { departement: "85", value: 4206 }, + { departement: "86", value: 7069 }, + { departement: "87", value: 7605 }, + { departement: "88", value: 100 }, + { departement: "89", value: 200 }, + { departement: "90", value: 300 }, + { departement: "91", value: 400 }, + { departement: "92", value: 500 }, + { departement: "93", value: 600 }, + { departement: "94", value: 700 }, + { departement: "95", value: 800 }, + { departement: "971", value: 900 }, + { departement: "972", value: 1000 }, + { departement: "973", value: 1000 }, + { departement: "974", value: 1200 }, + { departement: "975", value: 1300 }, + ]; + + const chartRef = useRef(null); + const [numResults, setNumResults] = useState(10); + + useEffect(() => { + const svg = d3.select(chartRef.current); + svg.selectAll("*").remove(); + + const margin = { top: 100, right: 10, bottom: 10, left: 10 }; + const width = 400 - margin.left - margin.right; + const height = 400 - margin.top - margin.bottom; + const padding = 20; + const thicknessOnMouseOver = 10; + const radius = Math.min(width, height) / 2 - padding; + + const sortedData = [...originalData].sort((a, b) => b.value - a.value); + + const displayedData = sortedData.slice(0, numResults); + + const totalSales = d3.sum(sortedData, (d) => d.value); + + const otherData = { + departement: "Autres", + value: totalSales - d3.sum(displayedData, (d) => d.value), + }; + + const pie = d3.pie() + .value((d) => d.value); + + const arc = d3.arc() + .outerRadius(radius) + .innerRadius(radius / 2); + + const colorScale = d3.scaleOrdinal(d3.schemeCategory10); + const g = svg.append("g") + .attr("transform", `translate(${width / 2},${height / 2})`); + + const arcs = g.selectAll("arc") + .data(pie([...displayedData, otherData])) + .enter().append("g") + .attr("class", "arc"); + + arcs.append("path") + .attr("d", arc) + .attr("fill", (d) => (d.data.departement === "Autres" ? "gray" : colorScale(d.data.departement))); + + + const outerArc = d3.arc() + .innerRadius(radius * 0.9) + .outerRadius(radius * 0.9); + + arcs.append("polyline") + .attr("stroke", "black") + .attr("stroke-width", 1) + .attr("fill", "none") + .attr("points", (d) => { + const posA = arc.centroid(d); + const posB = outerArc.centroid(d); + const posC = outerArc.centroid(d); + const midangle = d.startAngle + (d.endAngle - d.startAngle) / 2; + posC[0] = radius * 0.95 * (midangle < Math.PI ? 1 : -1); + return [posA, posB, posC]; + }); + + arcs.append("text") + .attr("transform", (d) => `translate(${outerArc.centroid(d)})`) + .attr("dy", ".35em") + .style("text-anchor", "middle") + .text((d) => { + const percentage = (d.data.value / totalSales) * 100; + return `${d.data.departement}: ${percentage.toFixed(2)}%`; + }); + svg.on("mouseover", function() { + const fillValue = this.getAttribute("fill"); + if (fillValue !== null) { + d3.select(this) + .transition() + .duration(200) + .attr("stroke", fillValue) + .attr("stroke-width", thicknessOnMouseOver); + this.parentNode.appendChild(this); + } + }) + .on("mouseout", function() { + d3.select(this) + .transition() + .attr("stroke", "none") + .attr("stroke-width", 0); + }); + const legend = svg.selectAll(".legend") + .data(pie(displayedData)) + .enter().append("g") + .attr("class", "legend") + .attr("transform", (d, i) => `translate(0,${i * 20})`); + + legend.append("rect") + .attr("x", width - 18) + .attr("width", 18) + .attr("height", 18) + .attr("fill", (d, i) => colorScale(i)); + + legend.append("text") + .attr("x", width - 24) + .attr("y", 9) + .attr("dy", ".35em") + .style("text-anchor", "end") + .text((d) => d.data.departement); + + }, [numResults]); + + return ( +
+ +
+ +
+ +
+ ); +}; + + +export default Pie_Chart; diff --git a/pwa/app/components/line_chart.tsx b/pwa/app/components/line_chart.tsx new file mode 100644 index 0000000..f52e8e9 --- /dev/null +++ b/pwa/app/components/line_chart.tsx @@ -0,0 +1,65 @@ +"use strict"; +import React, { useRef, useEffect } from "react"; +import * as d3 from "d3"; + +interface DataPoint { + year: number; + value: number; +} + +function Line_Chart() { + const chartRef = useRef(null); + + const data: DataPoint[] = [ + { year: 2018, value: 21050 }, + { year: 2019, value: 28203 }, + { year: 2020, value: 27420 }, + { year: 2021, value: 24128 }, + { year: 2022, value: 38806 }, + { year: 2023, value: 12999 }, + ]; + + useEffect(() => { + const width = 640; + const height = 400; + const marginTop = 20; + const marginRight = 20; + const marginBottom = 30; + const marginLeft = 40; + + const x = d3.scaleTime() + .domain([new Date("2018-01-01"), new Date("2023-01-01")]) + .range([marginLeft, width - marginRight]); + + const y = d3.scaleLinear() + .domain([0, d3.max(data, d => d.value)]) + .range([height - marginBottom, marginTop]); + + const svg = d3.select(chartRef.current) + .attr("width", width) + .attr("height", height); + + svg.append("g") + .attr("transform", `translate(0,${height - marginBottom})`) + .call(d3.axisBottom(x).ticks(d3.timeYear.every(1)).tickFormat(d3.timeFormat("%Y"))); + + svg.append("g") + .attr("transform", `translate(${marginLeft},0)`) + .call(d3.axisLeft(y)); + + const line = d3.line() + .x(d => x(new Date(`${d.year}-01-01`))) + .y(d => y(d.value)); + + svg.append("path") + .datum(data) + .attr("fill", "none") + .attr("stroke", "steelblue") + .attr("stroke-width", 2) + .attr("d", line); + }, [data]); + + return ; +} + +export default Line_Chart; diff --git a/pwa/app/page.jsx b/pwa/app/page.jsx index fc7f89d..4ba7904 100644 --- a/pwa/app/page.jsx +++ b/pwa/app/page.jsx @@ -1,30 +1,60 @@ "use client"; import React, {useEffect, useState} from "react"; +import config from '../config.js'; import FetchService from "./help/fetch-service"; -import Donut from "./components/donut"; +import Donut from "./components/donut.tsx"; import DateInput from "./components/date-input"; +import DataForm from "./components/bar-chart/form"; +import BarChart from "./components/bar-chart/chart"; +import Line_Chart from "./components/line_chart.tsx"; export default function Page() { - const [donutValues, setDonutValues] = useState([]); + const [donutValues, setDonutValues] = useState([]); + const [chartData, setChartData] = useState([]); + const [isLoading, setIsLoading] = useState(false); + const Loader = () =>
Loading...
; - useEffect(() => { - (async () => { - const data = await FetchService.getDonutContent(); - const formattedData = data.map((x) => ({ - id: x.region, - count: x.occurences - })); - setDonutValues(formattedData); - })(); - }, []); + const handleFormSubmit = async (formData) => { + setIsLoading(true); + const url = `${config.apiUrl}/bar-chart/${formData.startDate}/${formData.endDate}/${formData.granularity}?page=1`; + const response = await fetch(url); const data = await response.json(); + console.log(data); + setChartData(data); + setIsLoading(false); + }; - return ( - <> -

Coucou

- - - - ); + useEffect(() => { + (async () => { + const data = await FetchService.getDonutContent(); + const formattedData = data.map((x) => ({ + id: x.region, + count: x.occurences + })); + setDonutValues(formattedData); + })(); + }, []); + + return ( + <> +
+
+

L’évolution du prix de vente moyen du mètre carré

+ +
+
+

Nombre des ventes par période

+ {isLoading && } + + +
+
+

Répartition des ventes par régions

+ + +
+
+ + ); } diff --git a/pwa/app/style/style.css b/pwa/app/style/style.css new file mode 100644 index 0000000..f16df6e --- /dev/null +++ b/pwa/app/style/style.css @@ -0,0 +1,43 @@ +.card { + background-color: white; + border: 1px solid #ddd; + border-radius: 8px; + padding: 20px; + margin: 10px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); +} + +.card-container { + display: flex; + justify-content: center; + flex-wrap: wrap; +} + +.chart-container { + margin: 20px; +} + +.form-container { + display: flex; + flex-direction: row; + align-items: center; + padding: 20px; + gap: 10px; +} + +.form-container input, .form-container select, .form-container button { + flex: 1; + padding: 8px; + border: 1px solid #ccc; + border-radius: 4px; +} + +.form-container button { + background-color: #4CAF50; + color: white; + cursor: pointer; +} + +.form-container button:hover { + background-color: #45a049; +} diff --git a/pwa/config.js b/pwa/config.js new file mode 100644 index 0000000..4650549 --- /dev/null +++ b/pwa/config.js @@ -0,0 +1,5 @@ +const config = { + apiUrl: 'https://localhost/api', +}; + +export default config; diff --git a/pwa/next-env.d.ts b/pwa/next-env.d.ts new file mode 100644 index 0000000..4f11a03 --- /dev/null +++ b/pwa/next-env.d.ts @@ -0,0 +1,5 @@ +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/pwa/package-lock.json b/pwa/package-lock.json index fa987e9..5bc8f84 100644 --- a/pwa/package-lock.json +++ b/pwa/package-lock.json @@ -12,6 +12,11 @@ "next": "14.0.4", "react": "^18", "react-dom": "^18" + }, + "devDependencies": { + "@types/node": "20.10.6", + "@types/react": "18.2.46", + "typescript": "5.3.3" } }, "node_modules/@next/env": { @@ -162,6 +167,38 @@ "tslib": "^2.4.0" } }, + "node_modules/@types/node": { + "version": "20.10.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.6.tgz", + "integrity": "sha512-Vac8H+NlRNNlAmDfGUP7b5h/KA+AtWIzuXy0E6OyP8f1tCLYAtPvKRRDJjAPqhpCb0t6U2j7/xqAuLEebW2kiw==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/prop-types": { + "version": "15.7.11", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz", + "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==", + "dev": true + }, + "node_modules/@types/react": { + "version": "18.2.46", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.46.tgz", + "integrity": "sha512-nNCvVBcZlvX4NU1nRRNV/mFl1nNRuTuslAJglQsq+8ldXe5Xv0Wd2f7WTE3jOxhLH2BFfiZGC6GCp+kHQbgG+w==", + "dev": true, + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/scheduler": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz", + "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==", + "dev": true + }, "node_modules/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -205,6 +242,12 @@ "node": ">= 10" } }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "dev": true + }, "node_modules/d3": { "version": "7.8.5", "resolved": "https://registry.npmjs.org/d3/-/d3-7.8.5.tgz", @@ -812,6 +855,25 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, + "node_modules/typescript": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, "node_modules/watchpack": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", diff --git a/pwa/package.json b/pwa/package.json index 5d6a8d6..6be0e36 100644 --- a/pwa/package.json +++ b/pwa/package.json @@ -13,5 +13,10 @@ "next": "14.0.4", "react": "^18", "react-dom": "^18" + }, + "devDependencies": { + "@types/node": "20.10.6", + "@types/react": "18.2.46", + "typescript": "5.3.3" } } diff --git a/pwa/tsconfig.json b/pwa/tsconfig.json new file mode 100644 index 0000000..14bd9ea --- /dev/null +++ b/pwa/tsconfig.json @@ -0,0 +1,34 @@ +{ + "compilerOptions": { + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], + "allowJs": true, + "skipLibCheck": true, + "strict": false, + "noEmit": true, + "incremental": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "plugins": [ + { + "name": "next" + } + ] + }, + "include": [ + "next-env.d.ts", + ".next/types/**/*.ts", + "**/*.ts", + "**/*.tsx" + ], + "exclude": [ + "node_modules" + ] +} -- GitLab