diff --git a/lib/constatnts.ts b/lib/constatnts.ts new file mode 100644 index 0000000..f2b921f --- /dev/null +++ b/lib/constatnts.ts @@ -0,0 +1,4 @@ +/** @format */ + +export const MEMBERS_ON_PAGE = 8; +export const PATHS_TO_FILES = ["crew.json", "crew.yaml"]; diff --git a/lib/crew.ts b/lib/crew.ts index b49024d..96dd7fe 100644 --- a/lib/crew.ts +++ b/lib/crew.ts @@ -1,4 +1,21 @@ /** + * @format * @todo Prepare a method to return a list of crew members * @description The list should only include crew members aged 30 to 40 */ + +import { CrewMember } from "@/models/CrewMembers"; +import { MEMBERS_ON_PAGE, PATHS_TO_FILES } from "./constatnts"; +import { getLocalData } from "./getMockData"; + +export const getCrewMembers = (page: number) => + getLocalData(PATHS_TO_FILES).then((data) => { + const filteredMembers = data.filter((item: CrewMember) => item.age >= 30 && item.age <= 40); + + const membersOnPage = filteredMembers.slice( + (page - 1) * MEMBERS_ON_PAGE, + page * MEMBERS_ON_PAGE + ); + + return { membersOnPage, countMembers: filteredMembers.length }; + }); diff --git a/lib/getMockData.ts b/lib/getMockData.ts new file mode 100644 index 0000000..938eb72 --- /dev/null +++ b/lib/getMockData.ts @@ -0,0 +1,54 @@ +/** @format */ + +import fs from "fs"; +import yaml from "js-yaml"; +import { sortAlphabeticOrder } from "./sortAlphabeticOrder"; +import { CrewMember, JsonFileType, YamlFileType } from "@/models/CrewMembers"; + +export async function getLocalData(paths: string[]): Promise { + let objectData: CrewMember[] = []; + + paths.forEach((filePath) => { + const extension = filePath.split(".").pop(); + + const file = fs.readFileSync(`./${filePath}`, "utf8"); + + let parsedData = []; + let transformedObject: CrewMember[] = []; + + switch (extension) { + case "json": + parsedData = JSON.parse(file) as JsonFileType[]; + + transformedObject = parsedData.map( + ({ firstName, lastName, nationality, age, profession }) => ({ + fullName: `${firstName} ${lastName}`, + nationality, + age, + profession, + }) + ); + + objectData = [...objectData, ...transformedObject]; + + break; + + case "yaml": + parsedData = yaml.load(file) as YamlFileType[]; + + transformedObject = parsedData.map(({ name, nationality, years_old, occupation }) => ({ + fullName: name, + nationality, + age: years_old, + profession: occupation, + })); + + objectData = [...objectData, ...transformedObject]; + + break; + default: + throw new Error("Unsupported file extension"); + } + }); + return sortAlphabeticOrder(objectData); +} diff --git a/lib/sortAlphabeticOrder.ts b/lib/sortAlphabeticOrder.ts new file mode 100644 index 0000000..1aea6aa --- /dev/null +++ b/lib/sortAlphabeticOrder.ts @@ -0,0 +1,14 @@ +/** @format */ + +import { CrewMember } from "@/models/CrewMembers"; + +export const sortAlphabeticOrder = (objectData: CrewMember[]) => + objectData.sort(function (a, b) { + if (a.fullName < b.fullName) { + return -1; + } + if (a.fullName > b.fullName) { + return 1; + } + return 0; + }); diff --git a/models/CrewMembers.ts b/models/CrewMembers.ts new file mode 100644 index 0000000..3dd0908 --- /dev/null +++ b/models/CrewMembers.ts @@ -0,0 +1,30 @@ +/** @format */ + +export type JsonFileType = { + firstName: string; + lastName: string; + nationality: string; + age: number; + profession: string; +}; + +export type YamlFileType = { + name: string; + nationality: string; + years_old: number; + occupation: string; +}; + +export type FileType = JsonFileType | YamlFileType; + +export type CrewMember = { + fullName: string; + nationality: string; + age: number; + profession: string; +}; + +export interface SchemaCrewMembers { + members: CrewMember[] | []; + countMembers: number; +} diff --git a/package-lock.json b/package-lock.json index fd319c8..bdb447e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,11 +8,15 @@ "name": "winter-camp-2024", "version": "0.1.0", "dependencies": { + "@heroicons/react": "^2.1.1", + "@tanstack/react-query": "^5.17.5", + "axios": "^1.6.5", "next": "13.5.6", "react": "^18", "react-dom": "^18" }, "devDependencies": { + "@types/js-yaml": "^4.0.9", "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", @@ -113,6 +117,14 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@heroicons/react": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.1.1.tgz", + "integrity": "sha512-JyyN9Lo66kirbCMuMMRPtJxtKJoIsXKS569ebHGGRKbl8s4CtUfLnyKJxteA+vIKySocO4s1SkTkGS4xtG/yEA==", + "peerDependencies": { + "react": ">= 16" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.13", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", @@ -392,6 +404,36 @@ "tslib": "^2.4.0" } }, + "node_modules/@tanstack/query-core": { + "version": "5.17.4", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.17.4.tgz", + "integrity": "sha512-1J2vINgJ6OH1/qgN/ouDjnRZc67TnKQ2mlIODKQr36Aa2vjMdH6Fb3pIbCRh5N6u95q6z1olp90n22eTlp9Ykg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.17.5", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.17.5.tgz", + "integrity": "sha512-TwVmK2YHW/aG6eM5Xre7akBSZHrWWa5frwQR5Kg9ERIttlVWzUHrRcsMOlB5QsMgHtlauzzr6Rm6M8a0T9Iwdg==", + "dependencies": { + "@tanstack/query-core": "5.17.4" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18.0.0" + } + }, + "node_modules/@types/js-yaml": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", + "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", + "dev": true + }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", @@ -793,6 +835,11 @@ "has-symbols": "^1.0.3" } }, + "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/autoprefixer": { "version": "10.4.16", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.16.tgz", @@ -851,6 +898,16 @@ "node": ">=4" } }, + "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/axobject-query": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", @@ -1069,6 +1126,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/commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", @@ -1176,6 +1244,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", @@ -1907,6 +1983,25 @@ "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", "dev": true }, + "node_modules/follow-redirects": { + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz", + "integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==", + "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", @@ -1916,6 +2011,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/fraction.js": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", @@ -2826,6 +2934,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/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -3421,6 +3548,11 @@ "react-is": "^16.13.1" } }, + "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", @@ -4441,6 +4573,12 @@ "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", "dev": true }, + "@heroicons/react": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.1.1.tgz", + "integrity": "sha512-JyyN9Lo66kirbCMuMMRPtJxtKJoIsXKS569ebHGGRKbl8s4CtUfLnyKJxteA+vIKySocO4s1SkTkGS4xtG/yEA==", + "requires": {} + }, "@humanwhocodes/config-array": { "version": "0.11.13", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", @@ -4611,6 +4749,25 @@ "tslib": "^2.4.0" } }, + "@tanstack/query-core": { + "version": "5.17.4", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.17.4.tgz", + "integrity": "sha512-1J2vINgJ6OH1/qgN/ouDjnRZc67TnKQ2mlIODKQr36Aa2vjMdH6Fb3pIbCRh5N6u95q6z1olp90n22eTlp9Ykg==" + }, + "@tanstack/react-query": { + "version": "5.17.5", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.17.5.tgz", + "integrity": "sha512-TwVmK2YHW/aG6eM5Xre7akBSZHrWWa5frwQR5Kg9ERIttlVWzUHrRcsMOlB5QsMgHtlauzzr6Rm6M8a0T9Iwdg==", + "requires": { + "@tanstack/query-core": "5.17.4" + } + }, + "@types/js-yaml": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", + "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", + "dev": true + }, "@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", @@ -4904,6 +5061,11 @@ "has-symbols": "^1.0.3" } }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "autoprefixer": { "version": "10.4.16", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.16.tgz", @@ -4930,6 +5092,16 @@ "integrity": "sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==", "dev": true }, + "axios": { + "version": "1.6.5", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.5.tgz", + "integrity": "sha512-Ii012v05KEVuUoFWmMW/UQv9aRIc3ZwkWDcM+h5Il8izZCtRVpDUfwpoFf7eOtajT3QiGR4yDUx7lPqHJULgbg==", + "requires": { + "follow-redirects": "^1.15.4", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "axobject-query": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", @@ -5075,6 +5247,14 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, "commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", @@ -5153,6 +5333,11 @@ "object-keys": "^1.1.1" } }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + }, "dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -5730,6 +5915,11 @@ "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", "dev": true }, + "follow-redirects": { + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz", + "integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==" + }, "for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -5739,6 +5929,16 @@ "is-callable": "^1.1.3" } }, + "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==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, "fraction.js": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", @@ -6386,6 +6586,19 @@ "picomatch": "^2.3.1" } }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "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==", + "requires": { + "mime-db": "1.52.0" + } + }, "minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -6767,6 +6980,11 @@ "react-is": "^16.13.1" } }, + "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==" + }, "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 dd54a2f..060638b 100644 --- a/package.json +++ b/package.json @@ -9,19 +9,23 @@ "lint": "next lint" }, "dependencies": { + "@heroicons/react": "^2.1.1", + "@tanstack/react-query": "^5.17.5", + "axios": "^1.6.5", + "next": "13.5.6", "react": "^18", - "react-dom": "^18", - "next": "13.5.6" + "react-dom": "^18" }, "devDependencies": { - "typescript": "^5", + "@types/js-yaml": "^4.0.9", "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", "autoprefixer": "^10", + "eslint": "^8", + "eslint-config-next": "13.5.6", "postcss": "^8", "tailwindcss": "^3", - "eslint": "^8", - "eslint-config-next": "13.5.6" + "typescript": "^5" } } diff --git a/pages/_app.tsx b/pages/_app.tsx index 021681f..41186cd 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -1,6 +1,15 @@ -import '@/styles/globals.css' -import type { AppProps } from 'next/app' +/** @format */ + +import "@/styles/globals.css"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import type { AppProps } from "next/app"; export default function App({ Component, pageProps }: AppProps) { - return + const queryClient = new QueryClient(); + + return ( + + + + ); } diff --git a/pages/api/crew.ts b/pages/api/crew.ts index 0f56e1f..b6faf7c 100644 --- a/pages/api/crew.ts +++ b/pages/api/crew.ts @@ -1,3 +1,6 @@ +/** @format */ + +import { getCrewMembers } from "@/lib/crew"; import type { NextApiRequest, NextApiResponse } from "next"; /** @@ -5,6 +8,10 @@ import type { NextApiRequest, NextApiResponse } from "next"; * @description The endpoint should return a pagination of 8 users per page. The endpoint should accept a query parameter "page" to return the corresponding page. */ -export default function handler(req: NextApiRequest, res: NextApiResponse) { - res.status(200).json([]); +export default async function (req: NextApiRequest, res: NextApiResponse) { + const numberPage = Number(req.query.page); + + const data = await getCrewMembers(numberPage); + + return res.status(200).json(data); } diff --git a/pages/components/ACard/ACard.tsx b/pages/components/ACard/ACard.tsx new file mode 100644 index 0000000..9c2f8f0 --- /dev/null +++ b/pages/components/ACard/ACard.tsx @@ -0,0 +1,33 @@ +/** @format */ +import { Typography } from "../ATitle/Typography"; +import { ACardProps } from "./type"; + +export const ACard = ({ data }: ACardProps) => { + const { fullName, nationality, age, profession } = data; + + const backgroundStyles = `bg-[url('https://source.unsplash.com/random?sig=${Math.floor( + Math.random() * 10 + )}')]`; + + return ( +
+
+
+
+ avatar +
+ Name : {fullName} + Nationality : {nationality} + Age : {age} years + Profession : {profession} +
+
+ ); +}; diff --git a/pages/components/ACard/type.ts b/pages/components/ACard/type.ts new file mode 100644 index 0000000..c38cdd1 --- /dev/null +++ b/pages/components/ACard/type.ts @@ -0,0 +1,10 @@ +/** @format */ + +import { CrewMember } from "@/models/CrewMembers"; + +export interface ACardProps { + data: CrewMember; + width: number; + height: number; + backgroundImage: string; +} diff --git a/pages/components/ATitle/Typography.tsx b/pages/components/ATitle/Typography.tsx new file mode 100644 index 0000000..6966240 --- /dev/null +++ b/pages/components/ATitle/Typography.tsx @@ -0,0 +1,9 @@ +/** @format */ + +type TypographyProps = { + children: React.ReactNode; +}; + +export const Typography = ({ children }: TypographyProps) => ( +

{children}

+); diff --git a/pages/components/Loading/Loading.tsx b/pages/components/Loading/Loading.tsx new file mode 100644 index 0000000..3340288 --- /dev/null +++ b/pages/components/Loading/Loading.tsx @@ -0,0 +1,5 @@ +/** @format */ + +export default function Loading() { + return
Loading...
; +} diff --git a/pages/components/Pagination/Pagination.tsx b/pages/components/Pagination/Pagination.tsx new file mode 100644 index 0000000..6b6d47f --- /dev/null +++ b/pages/components/Pagination/Pagination.tsx @@ -0,0 +1,56 @@ +/** @format */ +"use client"; + +import { MEMBERS_ON_PAGE } from "@/lib/constatnts"; +import { ChevronLeftIcon, ChevronRightIcon } from "@heroicons/react/20/solid"; +import Link from "next/link"; + +export interface PaginationProps { + lengthArray: number; + page: number; +} + +export const Pagination = ({ lengthArray = 0, page }: PaginationProps) => { + const pages = Math.ceil(lengthArray / MEMBERS_ON_PAGE); + const pageNumbers = Array.from({ length: pages }, (_, i) => i + 1); + + return ( +
+ +
+ ); +}; diff --git a/pages/customHooks/useAstronautsList.ts b/pages/customHooks/useAstronautsList.ts new file mode 100644 index 0000000..ecb51a6 --- /dev/null +++ b/pages/customHooks/useAstronautsList.ts @@ -0,0 +1,13 @@ +/** @format */ +import { useQuery } from "@tanstack/react-query"; +import axios from "axios"; + +export const useAstronautsList = (page: number) => { + return useQuery({ + queryKey: ["astronauts", page], + queryFn: async () => { + const { data } = await axios.get(`/api/crew?page=${page}`); + return data; + }, + }); +}; diff --git a/pages/index.tsx b/pages/index.tsx index 2d922d3..f2ae4ab 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -1,21 +1,21 @@ +/** @format */ + import { Inter } from "next/font/google"; import Link from "next/link"; const inter = Inter({ subsets: ["latin"] }); export default function Home() { - return ( -
-
-

- Winter Camp 2024 Recruitment Task -

-
-
- Go to task -
-
- ); + return ( +
+
+

Mars mission 2024

+
+
+ Go to task +
+
+ ); } diff --git a/pages/task/[page].tsx b/pages/task/[page].tsx index 86f1097..1880f56 100644 --- a/pages/task/[page].tsx +++ b/pages/task/[page].tsx @@ -1,8 +1,56 @@ /** + * @format * @todo List crew members using the endpoint you created * @description Use tanstack/react-query or swr to fetch data from the endpoint. Prepare pagination. */ +import { useAstronautsList } from "../customHooks/useAstronautsList"; +import { Pagination } from "../components/Pagination/Pagination"; +import { ACard } from "../components/ACard/ACard"; +import { useRouter } from "next/router"; +import { CrewMember } from "@/models/CrewMembers"; + +const hoverStyles = [ + "hover:[&>div:not:hover>div:first::child]:blur-lg", + "hover:[&>div:not:hover>div:first::child]:brightness-50", + "hover:[&>div:not:hover>div:first::child]:contrast-80", + "hover:[&>div:not:hover>div:first::child]:saturate-0", + "hover:[&>div:not:hover>div:last::child>h2]:text-transparent", + "hover:[&>div:not:hover>div:last::child>h2]:shadow-md", +]; + export default function Task() { - return
Task
; + const { + query: { page }, + } = useRouter(); + const currentPage = page ? Number(page) : 1; + + const { status, data } = useAstronautsList(currentPage); + + return ( +
+ {status === "pending" ? ( +
Loading...
+ ) : ( + <> +
+ {data.membersOnPage?.map((astronaut: CrewMember, idx: number) => { + return ( + + ); + })} +
+ + + )} +
+ ); } diff --git a/pages/utils/transformToArray.ts b/pages/utils/transformToArray.ts new file mode 100644 index 0000000..1a1f0eb --- /dev/null +++ b/pages/utils/transformToArray.ts @@ -0,0 +1 @@ +/** @format */ diff --git a/styles/globals.css b/styles/globals.css index fd81e88..93628a6 100644 --- a/styles/globals.css +++ b/styles/globals.css @@ -1,27 +1,29 @@ +/** @format */ + @tailwind base; @tailwind components; @tailwind utilities; :root { - --foreground-rgb: 0, 0, 0; - --background-start-rgb: 214, 219, 220; - --background-end-rgb: 255, 255, 255; + --foreground-rgb: 0, 0, 0; + --background-start-rgb: 214, 219, 220; + --background-end-rgb: 255, 255, 255; } @media (prefers-color-scheme: dark) { - :root { - --foreground-rgb: 255, 255, 255; - --background-start-rgb: 0, 0, 0; - --background-end-rgb: 0, 0, 0; - } + :root { + --foreground-rgb: 255, 255, 255; + --background-start-rgb: 0, 0, 0; + --background-end-rgb: 0, 0, 0; + } } body { - color: rgb(var(--foreground-rgb)); - background: linear-gradient( - to bottom, - transparent, - rgb(var(--background-end-rgb)) - ) - rgb(var(--background-start-rgb)); + background: rgb(2, 0, 36); + background: linear-gradient( + 90deg, + rgba(2, 0, 36, 1) 0%, + rgba(121, 58, 9, 1) 100%, + rgba(0, 212, 255, 1) 100% + ); }