diff --git a/package-lock.json b/package-lock.json
index 15de32237a17a5d4fd4ecc7be6eced5d740791a2..082678053912b821f1d6530ec04b5985c9e97e37 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,6 +10,7 @@
"dependencies": {
"@tanstack/react-query": "^5.17.12",
"@types/node": "^20.11.3",
+ "axios": "^1.6.5",
"bootstrap": "^5.3.2",
"react": "^18.2.0",
"react-bootstrap": "^2.9.2",
@@ -2790,6 +2791,11 @@
"node": ">=8"
}
},
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+ },
"node_modules/available-typed-arrays": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz",
@@ -2802,6 +2808,16 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/axios": {
+ "version": "1.6.5",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.5.tgz",
+ "integrity": "sha512-Ii012v05KEVuUoFWmMW/UQv9aRIc3ZwkWDcM+h5Il8izZCtRVpDUfwpoFf7eOtajT3QiGR4yDUx7lPqHJULgbg==",
+ "dependencies": {
+ "follow-redirects": "^1.15.4",
+ "form-data": "^4.0.0",
+ "proxy-from-env": "^1.1.0"
+ }
+ },
"node_modules/babel-jest": {
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz",
@@ -3173,6 +3189,17 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -3334,6 +3361,14 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
"node_modules/dequal": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
@@ -3881,6 +3916,25 @@
"integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==",
"dev": true
},
+ "node_modules/follow-redirects": {
+ "version": "1.15.5",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz",
+ "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/RubenVerborgh"
+ }
+ ],
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependenciesMeta": {
+ "debug": {
+ "optional": true
+ }
+ }
+ },
"node_modules/for-each": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
@@ -3890,6 +3944,19 @@
"is-callable": "^1.1.3"
}
},
+ "node_modules/form-data": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+ "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@@ -5443,6 +5510,25 @@
"node": ">=8.6"
}
},
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
"node_modules/mimic-fn": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
@@ -5937,6 +6023,11 @@
"react": ">=0.14.0"
}
},
+ "node_modules/proxy-from-env": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+ },
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
diff --git a/package.json b/package.json
index 8ea1a649480157f47ce014626a87091a6db6cc80..e4b2c06a0da3108fb9824f51d18800c37f037c75 100644
--- a/package.json
+++ b/package.json
@@ -13,6 +13,7 @@
"dependencies": {
"@tanstack/react-query": "^5.17.12",
"@types/node": "^20.11.3",
+ "axios": "^1.6.5",
"bootstrap": "^5.3.2",
"react": "^18.2.0",
"react-bootstrap": "^2.9.2",
diff --git a/src/App.tsx b/src/App.tsx
index d8c16b60415000ebb3037cda29f9158f5716740a..d46c4ad3e229e9da40ac0ff1b34e9e3550129cd7 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -6,7 +6,7 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
const queryClient = new QueryClient();
function App() {
- const [selection, setSelection] = useState(0);
+ const [selection, setSelection] = useState(1);
return (
<QueryClientProvider client={queryClient}>
diff --git a/src/components/Formulaire.tsx b/src/components/Formulaire.tsx
index 8fcc33fd3ec63cd73d4bc3e33b4edd604f0e3797..943dec4d0110e73e0c93f5a15e147f9e4da45cd4 100644
--- a/src/components/Formulaire.tsx
+++ b/src/components/Formulaire.tsx
@@ -1,13 +1,12 @@
-import { useQuery } from "@tanstack/react-query";
-import { Button, Col, Container, Form, Row } from "react-bootstrap";
+import { Col, Container, Form, Row } from "react-bootstrap";
+import FormulaireArret from "./Formulaires/Formulaire-Arret";
type FormulaireProps = {
selection: number;
};
-const URL = `https://hackathon-login.osc-fr1.scalingo.io`;
-
const Formulaire = ({ selection }: FormulaireProps) => {
+
const getFormsGroups = (selection: number) => {
switch (selection) {
case 0:
@@ -19,7 +18,7 @@ const Formulaire = ({ selection }: FormulaireProps) => {
<Form.Label>Latitude</Form.Label>
<Form.Control
type="text"
- placeholder="47,264"
+ defaultValue="47,264"
pattern="-?\d+(,\d+)?(\.\d+)?"
step="any"
required
@@ -33,7 +32,7 @@ const Formulaire = ({ selection }: FormulaireProps) => {
<Form.Label>Longitude</Form.Label>
<Form.Control
type="text"
- placeholder="-1,585"
+ defaultValue="-1,585"
pattern="-?\d+(,\d+)?(\.\d+)?"
step="any"
required
@@ -46,11 +45,7 @@ const Formulaire = ({ selection }: FormulaireProps) => {
</>
);
case 1:
- return (
- <>
- <h1>Liste de tous les arrĂȘts</h1>
- </>
- );
+ return <FormulaireArret />;
case 2:
return (
<>
@@ -93,31 +88,10 @@ const Formulaire = ({ selection }: FormulaireProps) => {
}
};
- const { isPending, isError, error, data } = useQuery({
- queryKey: ["arrets"],
- queryFn: async () => {
- const result = await fetch(`${URL}/get_all_stations/`);
- return result.json();
- },
- });
-
- if (isPending) return <p>Loading...</p>;
- else if (isError)
- return (
- <p>
- Error fetching data: {error.name} - {error.message}
- </p>
- );
-
return (
<Container>
<Row>
- <Form onSubmit={(e) => { e.preventDefault(); console.log(data) }}>
{getFormsGroups(selection)}
- <Button className="mt-3" variant="primary" type="submit">
- Submit
- </Button>
- </Form>
</Row>
</Container>
);
diff --git a/src/components/Formulaires/Formulaire-Arret.tsx b/src/components/Formulaires/Formulaire-Arret.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..7597899d2aa557ac4aa44c2c7bc8d98704b1469a
--- /dev/null
+++ b/src/components/Formulaires/Formulaire-Arret.tsx
@@ -0,0 +1,88 @@
+import { keepPreviousData, useQuery } from "@tanstack/react-query";
+import { getStops, getStopsPaginated } from "../../utils";
+import { useState } from "react";
+import { Button, Col, Form, Spinner } from "react-bootstrap";
+
+const FormulaireArret = () => {
+ const [page, setPage] = useState(1);
+ const [filter, setFilter] = useState("");
+ const [showResult, setShowResult] = useState(false);
+
+ const { data: listArrets, isFetched } = useQuery({
+ queryKey: ["arrets"],
+ queryFn: async () => getStops(),
+ });
+
+ const { isPending, isError, error, data } = useQuery({
+ queryKey: ["arrets", { page, filter }],
+ placeholderData: keepPreviousData,
+ enabled: isFetched,
+ queryFn: () => getStopsPaginated(listArrets!, page, filter),
+ });
+
+ return (
+ <>
+ <Form
+ onSubmit={(e) => {
+ e.preventDefault();
+ setShowResult(true);
+ }}
+ >
+ <h1>Liste de tous les arrĂȘts</h1>
+ <Form.Group className="mb-3" controlId="ArretSearchBar">
+ <Form.Label>Barre de recherche</Form.Label>
+ <Form.Control
+ type="text"
+ placeholder="Ecrire ici pour filtrer"
+ onChange={(e) => {
+ setPage(1);
+ setFilter(e.target.value);
+ }}
+ />
+ </Form.Group>
+ <Button className="mt-1" variant="primary" type="submit">
+ Submit
+ </Button>
+ <Button
+ className="mt-1"
+ variant="primary"
+ type="reset"
+ onClick={() => setShowResult(false)}
+ >
+ Clear
+ </Button>
+ </Form>
+ {!showResult ? (
+ <></>
+ ) : isPending ? (
+ <Spinner animation="border" role="status">
+ <span className="visually-hidden">Loading...</span>
+ </Spinner>
+ ) : isError ? (
+ <p>
+ An error has occured: {error.name} - {error.message}
+ </p>
+ ) : (
+ <Col md={4} className="mt-3">
+ {data.arrets.map((arret) => (
+ <p key={arret.id}>{arret.libelle}</p>
+ ))}
+ <Button
+ disabled={data.previousPage === undefined}
+ onClick={() => setPage(page - 1)}
+ >
+ Previous
+ </Button>
+ <Button
+ disabled={data.nextPage === undefined}
+ onClick={() => setPage(page + 1)}
+ >
+ Next
+ </Button>
+ </Col>
+ )}
+ </>
+ );
+};
+
+export default FormulaireArret;
diff --git a/src/models/Arret.model.ts b/src/models/Arret.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..329c12af7a912adfe7e0680d4043ac33d03acd1a
--- /dev/null
+++ b/src/models/Arret.model.ts
@@ -0,0 +1,12 @@
+export type Arret =
+ {
+ id: number,
+ "codeLieu": string,
+ "libelle": string,
+ "distance": string,
+ "ligne": [
+ {
+ "numLigne": string
+ }
+ ]
+ }
\ No newline at end of file
diff --git a/src/utils.ts b/src/utils.ts
new file mode 100644
index 0000000000000000000000000000000000000000..41ee55304745d2242a3855b9b075854d92ebaaa7
--- /dev/null
+++ b/src/utils.ts
@@ -0,0 +1,26 @@
+import axios from "axios";
+import { Arret } from "./models/Arret.model";
+
+const URL = `https://hackathon-login.osc-fr1.scalingo.io`;
+
+export const getPaginated = (data: Array<object>, page: number) => {
+ const hasNext = page * 10 < data.length;
+ const startIndex = (page - 1) * 10;
+ const endIndex = startIndex + 10;
+ return {
+ nextPage: hasNext ? page + 1 : undefined,
+ previousPage: page > 1 ? page - 1 : undefined,
+ arrets: data.slice(startIndex, endIndex)
+ }
+}
+
+export const getStopsPaginated = (data: Arret[], page: number, filter: string = '') => {
+ const json = data.filter((arret) => arret.libelle.includes(filter));
+ const paginated = getPaginated(json, page);
+ return { ...paginated, arrets: paginated.arrets as Arret[] }
+}
+
+export const getStops = async () => {
+ const result = await axios.get<Omit<Arret, "id">[]>(`${URL}/get_all_stations/`);
+ return result.data.map((arret, i) => { return { ...arret, id: i } as Arret });
+}
\ No newline at end of file