diff --git a/.gitignore b/.gitignore index 6d439ed..f3c05b0 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,7 @@ build/ # Моки mocks.json + +# Директория с загруженными изображениями +src/express/upload/img/*.* +!src/express/upload/img/.gitkeep diff --git a/package-lock.json b/package-lock.json index e695db8..9b13bb9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -312,7 +312,8 @@ "@babel/helper-validator-identifier": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", - "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==" + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", + "dev": true }, "@babel/helpers": { "version": "7.12.1", @@ -380,7 +381,8 @@ "@babel/parser": { "version": "7.11.5", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.5.tgz", - "integrity": "sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q==" + "integrity": "sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q==", + "dev": true }, "@babel/plugin-syntax-async-generators": { "version": "7.8.4", @@ -632,6 +634,7 @@ "version": "7.11.5", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz", "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==", + "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", "lodash": "^4.17.19", @@ -641,7 +644,8 @@ "lodash": { "version": "4.17.20", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", + "dev": true } } }, @@ -939,6 +943,11 @@ "defer-to-connect": "^1.0.1" } }, + "@types/babel-types": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@types/babel-types/-/babel-types-7.0.9.tgz", + "integrity": "sha512-qZLoYeXSTgQuK1h7QQS16hqLGdmqtRmN8w/rl3Au/l5x/zkHx+a4VHrHyBsi1I1vtK2oBHxSzKIu0R5p6spdOA==" + }, "@types/babel__core": { "version": "7.1.10", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.10.tgz", @@ -980,6 +989,14 @@ "@babel/types": "^7.3.0" } }, + "@types/babylon": { + "version": "6.16.5", + "resolved": "https://registry.npmjs.org/@types/babylon/-/babylon-6.16.5.tgz", + "integrity": "sha512-xH2e58elpj1X4ynnKp9qSnWlsRTIs6n3tgLGNfwAGHwePw0mulHQllV34n0T25uYSu1k0hRKkWXF890B1yS47w==", + "requires": { + "@types/babel-types": "*" + } + }, "@types/color-name": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", @@ -1118,6 +1135,26 @@ "uri-js": "^4.2.2" } }, + "align-text": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", + "requires": { + "kind-of": "^3.0.2", + "longest": "^1.0.1", + "repeat-string": "^1.5.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, "ansi-align": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", @@ -1186,6 +1223,11 @@ "picomatch": "^2.0.4" } }, + "append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha1-HjRA6RXwsSA9I3SOeO3XubW0PlY=" + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -1290,11 +1332,6 @@ "safer-buffer": "~2.1.0" } }, - "assert-never": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/assert-never/-/assert-never-1.2.1.tgz", - "integrity": "sha512-TaTivMB6pYI1kXwrFlEhLeGfOqoDNdTxjCdwRfFFkEA30Eu+k48W34nlok2EYWJfFFzqaEmichdNM7th6M5HNw==" - }, "assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", @@ -1342,6 +1379,14 @@ "integrity": "sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA==", "dev": true }, + "axios": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.0.tgz", + "integrity": "sha512-fmkJBknJKoZwem3/IKSSLpkdNXZeBu5Q7GA/aRsr2btgrptmSCxi2oFjZHqGdK9DoTil9PIHlPIZw2EcRJXRvw==", + "requires": { + "follow-redirects": "^1.10.0" + } + }, "babel-jest": { "version": "26.6.1", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.6.1.tgz", @@ -1412,14 +1457,38 @@ "babel-preset-current-node-syntax": "^0.1.3" } }, - "babel-walk": { - "version": "3.0.0-canary-5", - "resolved": "https://registry.npmjs.org/babel-walk/-/babel-walk-3.0.0-canary-5.tgz", - "integrity": "sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw==", + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "requires": { - "@babel/types": "^7.9.6" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } }, + "babel-types": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", + "requires": { + "babel-runtime": "^6.26.0", + "esutils": "^2.0.2", + "lodash": "^4.17.4", + "to-fast-properties": "^1.0.3" + }, + "dependencies": { + "to-fast-properties": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", + "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=" + } + } + }, + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==" + }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -1618,8 +1687,39 @@ "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", - "dev": true + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + }, + "busboy": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz", + "integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=", + "requires": { + "dicer": "0.2.5", + "readable-stream": "1.1.x" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + } + } }, "bytes": { "version": "3.1.0", @@ -1702,6 +1802,15 @@ "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", "dev": true }, + "center-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", + "requires": { + "align-text": "^0.1.3", + "lazy-cache": "^1.0.3" + } + }, "chalk": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", @@ -1806,6 +1915,14 @@ } } }, + "clean-css": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz", + "integrity": "sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==", + "requires": { + "source-map": "~0.6.0" + } + }, "cli-boxes": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", @@ -1916,6 +2033,41 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, "configstore": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", @@ -1931,12 +2083,14 @@ } }, "constantinople": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-4.0.1.tgz", - "integrity": "sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-3.1.2.tgz", + "integrity": "sha512-yePcBqEFhLOqSBtwYOGGS1exHo/s1xjekXiinh4itpNQGCu4KA1euPh1fg07N2wMITZXQkBz75Ntdt1ctGZouw==", "requires": { - "@babel/parser": "^7.6.0", - "@babel/types": "^7.6.1" + "@types/babel-types": "^7.0.0", + "@types/babylon": "^6.16.2", + "babel-types": "^6.26.0", + "babylon": "^6.18.0" } }, "content-disposition": { @@ -1983,11 +2137,15 @@ "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", "dev": true }, + "core-js": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", + "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==" + }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, "cross-env": { "version": "7.0.2", @@ -2123,8 +2281,7 @@ "decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" }, "decimal.js": { "version": "10.2.1", @@ -2132,6 +2289,11 @@ "integrity": "sha512-KaL7+6Fw6i5A2XSnsbhm/6B+NuEA7TZ4vqxnd5tXz9sbKtrN9Srj8ab4vKVdK8YAqZO9P1kg45Y6YLoduPf+kw==", "dev": true }, + "decline-word": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/decline-word/-/decline-word-1.2.11.tgz", + "integrity": "sha512-VIKe5TKloRoIfku9NFoKaeZOf4O7W3K5OP2inCaIKtBZNcm0u722HJv4VkgYiOxXC71KjBQ7hpMwoleMQlLn4w==" + }, "decode-uri-component": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", @@ -2234,6 +2396,38 @@ "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", "dev": true }, + "dicer": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz", + "integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=", + "requires": { + "readable-stream": "1.1.x", + "streamsearch": "0.1.2" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + } + } + }, "diff-sequences": { "version": "26.5.0", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.5.0.tgz", @@ -2514,8 +2708,7 @@ "esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" }, "etag": { "version": "1.8.1", @@ -2925,6 +3118,11 @@ "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==", "dev": true }, + "follow-redirects": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz", + "integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==" + }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -3353,8 +3551,7 @@ "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "ini": { "version": "1.3.5", @@ -3460,8 +3657,7 @@ "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" }, "is-ci": { "version": "2.0.0", @@ -3528,18 +3724,18 @@ "optional": true }, "is-expression": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-4.0.0.tgz", - "integrity": "sha512-zMIXX63sxzG3XrkHkrAPvm/OVZVSCPNkwMHU8oTX7/U3AL78I0QXCEICXUM13BIa8TYGZ68PiTKfQz3yaTNr4A==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-3.0.0.tgz", + "integrity": "sha1-Oayqa+f9HzRx3ELHQW5hwkMXrJ8=", "requires": { - "acorn": "^7.1.1", - "object-assign": "^4.1.1" + "acorn": "~4.0.2", + "object-assign": "^4.0.1" }, "dependencies": { "acorn": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.0.tgz", - "integrity": "sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w==" + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", + "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=" } } }, @@ -3675,8 +3871,7 @@ "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "isexe": { "version": "2.0.0", @@ -4458,6 +4653,11 @@ "package-json": "^6.3.0" } }, + "lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=" + }, "leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -4492,8 +4692,7 @@ "lodash": { "version": "4.17.20", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", - "dev": true + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" }, "lodash.sortby": { "version": "4.7.0", @@ -4501,6 +4700,11 @@ "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", "dev": true }, + "longest": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=" + }, "lowercase-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", @@ -4613,8 +4817,7 @@ "minimist": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" }, "mixin-deep": { "version": "1.3.2", @@ -4641,7 +4844,6 @@ "version": "0.5.5", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, "requires": { "minimist": "^1.2.5" } @@ -4658,6 +4860,21 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, + "multer": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.2.tgz", + "integrity": "sha512-xY8pX7V+ybyUpbYMxtjM9KAiD9ixtg5/JkeKUTD6xilfDv0vzzOFcCp4Ljb1UU3tSOM3VTZtKo63OmzOrGi3Cg==", + "requires": { + "append-field": "^1.0.0", + "busboy": "^0.2.11", + "concat-stream": "^1.5.2", + "mkdirp": "^0.5.1", + "object-assign": "^4.1.1", + "on-finished": "^2.3.0", + "type-is": "^1.6.4", + "xtend": "^4.0.0" + } + }, "mute-stream": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", @@ -5214,6 +5431,11 @@ } } }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, "progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", @@ -5260,116 +5482,118 @@ "dev": true }, "pug": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pug/-/pug-3.0.0.tgz", - "integrity": "sha512-inmsJyFBSHZaiGLaguoFgJGViX0If6AcfcElimvwj9perqjDpUpw79UIEDZbWFmoGVidh08aoE+e8tVkjVJPCw==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pug/-/pug-2.0.4.tgz", + "integrity": "sha512-XhoaDlvi6NIzL49nu094R2NA6P37ijtgMDuWE+ofekDChvfKnzFal60bhSdiy8y2PBO6fmz3oMEIcfpBVRUdvw==", "requires": { - "pug-code-gen": "^3.0.0", - "pug-filters": "^4.0.0", - "pug-lexer": "^5.0.0", - "pug-linker": "^4.0.0", - "pug-load": "^3.0.0", - "pug-parser": "^6.0.0", - "pug-runtime": "^3.0.0", - "pug-strip-comments": "^2.0.0" + "pug-code-gen": "^2.0.2", + "pug-filters": "^3.1.1", + "pug-lexer": "^4.1.0", + "pug-linker": "^3.0.6", + "pug-load": "^2.0.12", + "pug-parser": "^5.0.1", + "pug-runtime": "^2.0.5", + "pug-strip-comments": "^1.0.4" } }, "pug-attrs": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pug-attrs/-/pug-attrs-3.0.0.tgz", - "integrity": "sha512-azINV9dUtzPMFQktvTXciNAfAuVh/L/JCl0vtPCwvOA21uZrC08K/UnmrL+SXGEVc1FwzjW62+xw5S/uaLj6cA==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pug-attrs/-/pug-attrs-2.0.4.tgz", + "integrity": "sha512-TaZ4Z2TWUPDJcV3wjU3RtUXMrd3kM4Wzjbe3EWnSsZPsJ3LDI0F3yCnf2/W7PPFF+edUFQ0HgDL1IoxSz5K8EQ==", "requires": { - "constantinople": "^4.0.1", - "js-stringify": "^1.0.2", - "pug-runtime": "^3.0.0" + "constantinople": "^3.0.1", + "js-stringify": "^1.0.1", + "pug-runtime": "^2.0.5" } }, "pug-code-gen": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-3.0.1.tgz", - "integrity": "sha512-xJIGvmXTQlkJllq6hqxxjRWcay2F9CU69TuAuiVZgHK0afOhG5txrQOcZyaPHBvSWCU/QQOqEp5XCH94rRZpBQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-2.0.2.tgz", + "integrity": "sha512-kROFWv/AHx/9CRgoGJeRSm+4mLWchbgpRzTEn8XCiwwOy6Vh0gAClS8Vh5TEJ9DBjaP8wCjS3J6HKsEsYdvaCw==", "requires": { - "constantinople": "^4.0.1", + "constantinople": "^3.1.2", "doctypes": "^1.1.0", - "js-stringify": "^1.0.2", - "pug-attrs": "^3.0.0", - "pug-error": "^2.0.0", - "pug-runtime": "^3.0.0", - "void-elements": "^3.1.0", - "with": "^7.0.0" + "js-stringify": "^1.0.1", + "pug-attrs": "^2.0.4", + "pug-error": "^1.3.3", + "pug-runtime": "^2.0.5", + "void-elements": "^2.0.1", + "with": "^5.0.0" } }, "pug-error": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-2.0.0.tgz", - "integrity": "sha512-sjiUsi9M4RAGHktC1drQfCr5C5eriu24Lfbt4s+7SykztEOwVZtbFk1RRq0tzLxcMxMYTBR+zMQaG07J/btayQ==" + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-1.3.3.tgz", + "integrity": "sha512-qE3YhESP2mRAWMFJgKdtT5D7ckThRScXRwkfo+Erqga7dyJdY3ZquspprMCj/9sJ2ijm5hXFWQE/A3l4poMWiQ==" }, "pug-filters": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/pug-filters/-/pug-filters-4.0.0.tgz", - "integrity": "sha512-yeNFtq5Yxmfz0f9z2rMXGw/8/4i1cCFecw/Q7+D0V2DdtII5UvqE12VaZ2AY7ri6o5RNXiweGH79OCq+2RQU4A==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/pug-filters/-/pug-filters-3.1.1.tgz", + "integrity": "sha512-lFfjNyGEyVWC4BwX0WyvkoWLapI5xHSM3xZJFUhx4JM4XyyRdO8Aucc6pCygnqV2uSgJFaJWW3Ft1wCWSoQkQg==", "requires": { - "constantinople": "^4.0.1", + "clean-css": "^4.1.11", + "constantinople": "^3.0.1", "jstransformer": "1.0.0", - "pug-error": "^2.0.0", - "pug-walk": "^2.0.0", - "resolve": "^1.15.1" + "pug-error": "^1.3.3", + "pug-walk": "^1.1.8", + "resolve": "^1.1.6", + "uglify-js": "^2.6.1" } }, "pug-lexer": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-5.0.0.tgz", - "integrity": "sha512-52xMk8nNpuyQ/M2wjZBN5gXQLIylaGkAoTk5Y1pBhVqaopaoj8Z0iVzpbFZAqitL4RHNVDZRnJDsqEYe99Ti0A==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-4.1.0.tgz", + "integrity": "sha512-i55yzEBtjm0mlplW4LoANq7k3S8gDdfC6+LThGEvsK4FuobcKfDAwt6V4jKPH9RtiE3a2Akfg5UpafZ1OksaPA==", "requires": { - "character-parser": "^2.2.0", - "is-expression": "^4.0.0", - "pug-error": "^2.0.0" + "character-parser": "^2.1.1", + "is-expression": "^3.0.0", + "pug-error": "^1.3.3" } }, "pug-linker": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-4.0.0.tgz", - "integrity": "sha512-gjD1yzp0yxbQqnzBAdlhbgoJL5qIFJw78juN1NpTLt/mfPJ5VgC4BvkoD3G23qKzJtIIXBbcCt6FioLSFLOHdw==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-3.0.6.tgz", + "integrity": "sha512-bagfuHttfQOpANGy1Y6NJ+0mNb7dD2MswFG2ZKj22s8g0wVsojpRlqveEQHmgXXcfROB2RT6oqbPYr9EN2ZWzg==", "requires": { - "pug-error": "^2.0.0", - "pug-walk": "^2.0.0" + "pug-error": "^1.3.3", + "pug-walk": "^1.1.8" } }, "pug-load": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-3.0.0.tgz", - "integrity": "sha512-OCjTEnhLWZBvS4zni/WUMjH2YSUosnsmjGBB1An7CsKQarYSWQ0GCVyd4eQPMFJqZ8w9xgs01QdiZXKVjk92EQ==", + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-2.0.12.tgz", + "integrity": "sha512-UqpgGpyyXRYgJs/X60sE6SIf8UBsmcHYKNaOccyVLEuT6OPBIMo6xMPhoJnqtB3Q3BbO4Z3Bjz5qDsUWh4rXsg==", "requires": { - "object-assign": "^4.1.1", - "pug-walk": "^2.0.0" + "object-assign": "^4.1.0", + "pug-walk": "^1.1.8" } }, "pug-parser": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-6.0.0.tgz", - "integrity": "sha512-ukiYM/9cH6Cml+AOl5kETtM9NR3WulyVP2y4HOU45DyMim1IeP/OOiyEWRr6qk5I5klpsBnbuHpwKmTx6WURnw==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-5.0.1.tgz", + "integrity": "sha512-nGHqK+w07p5/PsPIyzkTQfzlYfuqoiGjaoqHv1LjOv2ZLXmGX1O+4Vcvps+P4LhxZ3drYSljjq4b+Naid126wA==", "requires": { - "pug-error": "^2.0.0", - "token-stream": "1.0.0" + "pug-error": "^1.3.3", + "token-stream": "0.0.1" } }, "pug-runtime": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pug-runtime/-/pug-runtime-3.0.0.tgz", - "integrity": "sha512-GoEPcmQNnaTsePEdVA05bDpY+Op5VLHKayg08AQiqJBWU/yIaywEYv7TetC5dEQS3fzBBoyb2InDcZEg3mPTIA==" + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/pug-runtime/-/pug-runtime-2.0.5.tgz", + "integrity": "sha512-P+rXKn9un4fQY77wtpcuFyvFaBww7/91f3jHa154qU26qFAnOe6SW1CbIDcxiG5lLK9HazYrMCCuDvNgDQNptw==" }, "pug-strip-comments": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-2.0.0.tgz", - "integrity": "sha512-zo8DsDpH7eTkPHCXFeAk1xZXJbyoTfdPlNR0bK7rpOMuhBYb0f5qUVCO1xlsitYd3w5FQTK7zpNVKb3rZoUrrQ==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-1.0.4.tgz", + "integrity": "sha512-i5j/9CS4yFhSxHp5iKPHwigaig/VV9g+FgReLJWWHEHbvKsbqL0oP/K5ubuLco6Wu3Kan5p7u7qk8A4oLLh6vw==", "requires": { - "pug-error": "^2.0.0" + "pug-error": "^1.3.3" } }, "pug-walk": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-2.0.0.tgz", - "integrity": "sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ==" + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-1.1.8.tgz", + "integrity": "sha512-GMu3M5nUL3fju4/egXwZO0XLi6fW/K3T3VTgFQ14GxNi8btlxgT5qZL//JwZFm/2Fa64J/PNS8AZeys3wiMkVA==" }, "pump": { "version": "3.0.0", @@ -5505,6 +5729,11 @@ "picomatch": "^2.2.1" } }, + "regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" + }, "regex-not": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", @@ -5554,8 +5783,7 @@ "repeat-string": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "dev": true + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" }, "request": { "version": "2.88.2", @@ -5715,6 +5943,14 @@ "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", "dev": true }, + "right-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", + "requires": { + "align-text": "^0.1.1" + } + }, "rimraf": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", @@ -6218,8 +6454,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" }, "source-map-resolve": { "version": "0.5.3", @@ -6372,6 +6607,11 @@ "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", "dev": true }, + "streamsearch": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", + "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=" + }, "string-length": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.1.tgz", @@ -6657,7 +6897,8 @@ "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true }, "to-object-path": { "version": "0.3.0", @@ -6712,9 +6953,9 @@ "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" }, "token-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-1.0.0.tgz", - "integrity": "sha1-zCAOqyYT9BZtJ/+a/HylbUnfbrQ=" + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-0.0.1.tgz", + "integrity": "sha1-zu78cXp2xDFvEm0LnbqlXX598Bo=" }, "touch": { "version": "3.1.0", @@ -6796,6 +7037,11 @@ "mime-types": "~2.1.24" } }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, "typedarray-to-buffer": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", @@ -6805,6 +7051,55 @@ "is-typedarray": "^1.0.0" } }, + "uglify-js": { + "version": "2.8.29", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "requires": { + "source-map": "~0.5.1", + "uglify-to-browserify": "~1.0.0", + "yargs": "~3.10.0" + }, + "dependencies": { + "camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=" + }, + "cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "requires": { + "center-align": "^0.1.1", + "right-align": "^0.1.1", + "wordwrap": "0.0.2" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "requires": { + "camelcase": "^1.0.2", + "cliui": "^2.1.0", + "decamelize": "^1.0.0", + "window-size": "0.1.0" + } + } + } + }, + "uglify-to-browserify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", + "optional": true + }, "undefsafe": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.3.tgz", @@ -6988,8 +7283,7 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "utils-merge": { "version": "1.0.1", @@ -7055,9 +7349,9 @@ } }, "void-elements": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", - "integrity": "sha1-YU9/v42AHwu18GYfWy9XhXUOTwk=" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", + "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=" }, "w3c-hr-time": { "version": "1.0.2", @@ -7142,15 +7436,40 @@ "string-width": "^4.0.0" } }, + "window-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=" + }, "with": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/with/-/with-7.0.2.tgz", - "integrity": "sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/with/-/with-5.1.1.tgz", + "integrity": "sha1-+k2qktrzLE6pTtRTyB8EaGtXXf4=", "requires": { - "@babel/parser": "^7.9.6", - "@babel/types": "^7.9.6", - "assert-never": "^1.2.1", - "babel-walk": "3.0.0-canary-5" + "acorn": "^3.1.0", + "acorn-globals": "^3.0.0" + }, + "dependencies": { + "acorn": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", + "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=" + }, + "acorn-globals": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-3.1.0.tgz", + "integrity": "sha1-/YJw9x+7SZawBPqIDuXUZXOnMb8=", + "requires": { + "acorn": "^4.0.4" + }, + "dependencies": { + "acorn": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", + "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=" + } + } + } } }, "word-wrap": { @@ -7159,6 +7478,11 @@ "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", "dev": true }, + "wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=" + }, "wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -7256,6 +7580,11 @@ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", "dev": true }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, "y18n": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", diff --git a/package.json b/package.json index 288bc16..672c2f8 100644 --- a/package.json +++ b/package.json @@ -33,12 +33,15 @@ "supertest": "5.0.0" }, "dependencies": { + "axios": "0.21.0", "chalk": "4.1.0", "cross-env": "7.0.2", + "decline-word": "1.2.11", "express": "4.17.1", "http-status-codes": "2.1.4", + "multer": "1.4.2", "nanoid": "3.1.12", "pino": "6.7.0", - "pug": "3.0.0" + "pug": "2.0.4" } } diff --git a/src/constants.js b/src/constants.js index 8ce1077..5916d4f 100644 --- a/src/constants.js +++ b/src/constants.js @@ -3,10 +3,9 @@ module.exports = { DEFAULT_COMMAND: `--help`, DEFAULT_PORT: 8080, - DEFAULT_LOCAL_PORT: 3000, + DEFAULT_API_PORT: 3000, FILE_NAME: `mocks.json`, GENERATED_ID_LENGTH: 6, - PUBLIC_DIR: `public`, USER_ARGV_INDEX: 2, Env: { DEVELOPMENT: `development`, diff --git a/src/express/api.js b/src/express/api.js new file mode 100644 index 0000000..1519a6d --- /dev/null +++ b/src/express/api.js @@ -0,0 +1,51 @@ +'use strict'; + +const axios = require(`axios`); +const {DEFAULT_API_PORT} = require(`../constants`); + +const TIMEOUT = 10000; +const port = process.env.API_PORT || DEFAULT_API_PORT; +const defaultUrl = `http://localhost:${port}/api/`; + +class API { + constructor(baseURL, timeout) { + this._http = axios.create({ + baseURL, + timeout + }); + } + + async _load(url, options) { + const {data} = await this._http.request({url, ...options}); + return data; + } + + getOffers() { + return this._load(`/offers`); + } + + getOffer(id) { + return this._load(`/offers/${id}`); + } + + search(query) { + return this._load(`/search`, {params: {query}}); + } + + async getCategories() { + return this._load(`/category`); + } + + async createOffer(data) { + return this._load(`/offers`, { + method: `POST`, + data + }); + } +} + +const defaultApi = new API(defaultUrl, TIMEOUT); +module.exports = { + API, + getAPI: () => defaultApi +}; diff --git a/src/express/index.js b/src/express/index.js index e0ba6a5..296e1c7 100644 --- a/src/express/index.js +++ b/src/express/index.js @@ -2,24 +2,29 @@ const express = require(`express`); const path = require(`path`); -const {DEFAULT_PORT, PUBLIC_DIR} = require(`../constants`); +const {StatusCodes} = require(`http-status-codes`); +const {DEFAULT_PORT} = require(`../constants`); const offersRouter = require(`./routes/offers`); const myRouter = require(`./routes/my`); -const indexRouter = require(`./routes/index`); +const mainRouter = require(`./routes/main`); + +const PUBLIC_DIR = `public`; +const UPLOAD_DIR = `upload`; const app = express(); app.use(`/offers`, offersRouter); app.use(`/my`, myRouter); -app.use(`/`, indexRouter); +app.use(`/`, mainRouter); app.use(express.static(path.resolve(__dirname, PUBLIC_DIR))); +app.use(express.static(path.resolve(__dirname, UPLOAD_DIR))); -app.use((req, res) => res.status(400).render(`400`)); +app.use((req, res) => res.status(StatusCodes.BAD_REQUEST).render(`400`)); app.use((err, req, res, _next) => { - res.status(500).render(`500`); + res.status(StatusCodes.INTERNAL_SERVER_ERROR).render(`500`); }); app.set(`views`, path.resolve(__dirname, `templates/entries`)); app.set(`view engine`, `pug`); -app.listen(DEFAULT_PORT); +app.listen(process.env.PORT || DEFAULT_PORT); diff --git a/src/express/lib/offers.js b/src/express/lib/offers.js new file mode 100644 index 0000000..41ebba4 --- /dev/null +++ b/src/express/lib/offers.js @@ -0,0 +1,17 @@ +'use strict'; + +// Набор общих функций для работы с объявлениями + +const {splitNumByThousands} = require(`../../utils`); + +// Доработка одиночного объявления для шаблонизации +const modifyOffer = (offer) => { + offer.colorIndex = offer.picture.match(/(\d+)\.jpg$/)[1]; + offer.retinaPicture = offer.picture.replace(`.jpg`, `@2x.jpg`); + offer.outputPrice = splitNumByThousands(offer.sum); + return offer; +}; + +module.exports = { + modifyOffer +}; diff --git a/src/express/routes/index.js b/src/express/routes/index.js deleted file mode 100644 index 8d378c8..0000000 --- a/src/express/routes/index.js +++ /dev/null @@ -1,22 +0,0 @@ -'use strict'; - -const {Router} = require(`express`); -const indexRouter = new Router(); - -indexRouter.get(`/login`, (req, res) => { - res.render(`login`); -}); - -indexRouter.get(`/register`, (req, res) => { - res.render(`sign-up`); -}); - -indexRouter.get(`/search`, (req, res) => { - res.render(`search-result`); -}); - -indexRouter.get(`/`, (req, res) => { - res.render(`main`); -}); - -module.exports = indexRouter; diff --git a/src/express/routes/main.js b/src/express/routes/main.js new file mode 100644 index 0000000..fa8fa5f --- /dev/null +++ b/src/express/routes/main.js @@ -0,0 +1,39 @@ +'use strict'; + +const {Router} = require(`express`); +const {modifyOffer} = require(`../lib/offers`); +const declineWord = require(`decline-word`); + +const mainRouter = new Router(); +const api = require(`../api`).getAPI(); + +mainRouter.get(`/`, async (req, res) => { + const offers = await api.getOffers(); + offers.forEach(modifyOffer); + res.render(`main`, {offers}); +}); + +mainRouter.get(`/search`, async (req, res) => { + try { + const results = await api.search(req.query.search); + results.forEach(modifyOffer); + + res.render(`search-result`, { + results, + findWord: declineWord(results.length, `Найден`, `а`, `о`, `о`), + pubWord: declineWord(results.length, `публикаци`, `я`, `и`, `й`) + }); + } catch (error) { + res.render(`search-result`, {results: []}); + } +}); + +mainRouter.get(`/login`, (req, res) => { + res.render(`login`); +}); + +mainRouter.get(`/register`, (req, res) => { + res.render(`sign-up`); +}); + +module.exports = mainRouter; diff --git a/src/express/routes/my.js b/src/express/routes/my.js index dec98ad..e40ef0c 100644 --- a/src/express/routes/my.js +++ b/src/express/routes/my.js @@ -1,14 +1,22 @@ 'use strict'; const {Router} = require(`express`); +const {modifyOffer} = require(`../lib/offers`); + const myRouter = new Router(); +const api = require(`../api`).getAPI(); -myRouter.get(`/`, (req, res) => { - res.render(`my-tickets`); +myRouter.get(`/`, async (req, res) => { + const offers = await api.getOffers(); + offers.forEach(modifyOffer); + res.render(`my-tickets`, {offers}); }); -myRouter.get(`/comments`, (req, res) => { - res.render(`comments`); +myRouter.get(`/comments`, async (req, res) => { + const offers = await api.getOffers(); + const restrictedOffers = offers.slice(0, 3); + restrictedOffers.forEach(modifyOffer); + res.render(`comments`, {offers: restrictedOffers}); }); module.exports = myRouter; diff --git a/src/express/routes/offers.js b/src/express/routes/offers.js index e60a7a4..70751a8 100644 --- a/src/express/routes/offers.js +++ b/src/express/routes/offers.js @@ -1,22 +1,65 @@ 'use strict'; const {Router} = require(`express`); +const multer = require(`multer`); +const path = require(`path`); +const {nanoid} = require(`nanoid`); +const {modifyOffer} = require(`../lib/offers`); + +const UPLOAD_DIR = `../upload/img/`; +const uploadDirAbsolute = path.resolve(__dirname, UPLOAD_DIR); const offersRouter = new Router(); +const api = require(`../api`).getAPI(); + +const upload = multer({ + storage: multer.diskStorage({ + destination: uploadDirAbsolute, + filename: (req, file, cb) => { + const uniqueName = nanoid(10); + const extension = file.originalname.split(`.`).pop(); + cb(null, `${uniqueName}.${extension}`); + } + }) +}); offersRouter.get(`/category/:id`, (req, res) => { res.render(`category`); }); -offersRouter.get(`/edit/:id`, (req, res) => { - res.render(`ticket-edit`); +offersRouter.get(`/edit/:id`, async (req, res) => { + const {id} = req.params; + const [offer, categories] = await Promise.all([ + api.getOffer(id), + api.getCategories() + ]); + res.render(`ticket-edit`, {offer: modifyOffer(offer), categories}); }); -offersRouter.get(`/add`, (req, res) => { - res.render(`new-ticket`); +offersRouter.get(`/add`, async (req, res) => { + const categories = await api.getCategories(); + res.render(`new-ticket`, {categories}); }); offersRouter.get(`/:id`, (req, res) => { res.render(`ticket`); }); +offersRouter.post(`/add`, upload.single(`avatar`), async (req, res) => { + const {body, file} = req; + const offerData = { + picture: file.filename, + sum: body.price, + type: body.action, + description: body.comment, + title: body[`ticket-name`], + category: body.category + }; + try { + await api.createOffer(offerData); + res.redirect(`/my`); + } catch (err) { + res.redirect(`back`); + } +}); + module.exports = offersRouter; diff --git a/src/express/templates/entries/category.pug b/src/express/templates/entries/category.pug index e2e79e8..7c943f8 100644 --- a/src/express/templates/entries/category.pug +++ b/src/express/templates/entries/category.pug @@ -13,14 +13,14 @@ block content li.tickets-list__item .ticket-card.ticket-card--color06 .ticket-card__img - img(src='/img/item06.jpg', srcset='/img/item06@2x.jpg 2x', alt='Изображение товара') + img(src=`/img/item06.jpg`, srcset=`/img/item06@2x.jpg 2x`, alt=`Изображение товара`) .ticket-card__info span.ticket-card__label ПРОДАМ .ticket-card__categories - a(href='#!') ЭЛЕКТРОНИКА + a(href=`#!`) ЭЛЕКТРОНИКА .ticket-card__header h3.ticket-card__title - a(href='#!') Ableton + a(href=`#!`) Ableton p.ticket-card__price span.js-sum 88 000 | ₽ @@ -29,14 +29,14 @@ block content li.tickets-list__item .ticket-card.ticket-card--color08 .ticket-card__img - img(src='/img/item08.jpg', srcset='/img/item08@2x.jpg 2x', alt='Изображение товара') + img(src=`/img/item08.jpg`, srcset=`/img/item08@2x.jpg 2x`, alt=`Изображение товара`) .ticket-card__info span.ticket-card__label Куплю .ticket-card__categories - a(href='#!') ЭЛЕКТРОНИКА + a(href=`#!`) ЭЛЕКТРОНИКА .ticket-card__header h3.ticket-card__title - a(href='#!') Фотик Canon + a(href=`#!`) Фотик Canon p.ticket-card__price span.js-sum 32 000 | ₽ @@ -45,14 +45,14 @@ block content li.tickets-list__item .ticket-card.ticket-card--color15 .ticket-card__img - img(src='/img/item15.jpg', srcset='/img/item15@2x.jpg 2x', alt='Изображение товара') + img(src=`/img/item15.jpg`, srcset=`/img/item15@2x.jpg 2x`, alt=`Изображение товара`) .ticket-card__info span.ticket-card__label ПРОДАМ .ticket-card__categories - a(href='#!') Электроника + a(href=`#!`) Электроника .ticket-card__header h3.ticket-card__title - a(href='#!') Кофемашина + a(href=`#!`) Кофемашина p.ticket-card__price span.js-sum 26 000 | ₽ @@ -61,14 +61,14 @@ block content li.tickets-list__item .ticket-card.ticket-card--color12 .ticket-card__img - img(src='/img/item12.jpg', srcset='/img/item12@2x.jpg 2x', alt='Изображение товара') + img(src=`/img/item12.jpg`, srcset=`/img/item12@2x.jpg 2x`, alt=`Изображение товара`) .ticket-card__info span.ticket-card__label Куплю .ticket-card__categories - a(href='#!') ЭЛЕКТРОНИКА + a(href=`#!`) ЭЛЕКТРОНИКА .ticket-card__header h3.ticket-card__title - a(href='#!') Радио Панасоник + a(href=`#!`) Радио Панасоник p.ticket-card__price span.js-sum 32 000 | ₽ @@ -77,14 +77,14 @@ block content li.tickets-list__item .ticket-card.ticket-card--color13 .ticket-card__img - img(src='/img/item13.jpg', srcset='/img/item13@2x.jpg 2x', alt='Изображение товара') + img(src=`/img/item13.jpg`, srcset=`/img/item13@2x.jpg 2x`, alt=`Изображение товара`) .ticket-card__info span.ticket-card__label ПРОДАМ .ticket-card__categories - a(href='#!') ЭЛЕКТРОНИКА + a(href=`#!`) ЭЛЕКТРОНИКА .ticket-card__header h3.ticket-card__title - a(href='#!') Штатив Sony + a(href=`#!`) Штатив Sony p.ticket-card__price span.js-sum 8 000 | ₽ @@ -93,14 +93,14 @@ block content li.tickets-list__item .ticket-card.ticket-card--color14 .ticket-card__img - img(src='/img/item14.jpg', srcset='/img/item14@2x.jpg 2x', alt='Изображение товара') + img(src=`/img/item14.jpg`, srcset=`/img/item14@2x.jpg 2x`, alt=`Изображение товара`) .ticket-card__info span.ticket-card__label Куплю .ticket-card__categories - a(href='#!') ЭЛЕКТРОНИКА + a(href=`#!`) ЭЛЕКТРОНИКА .ticket-card__header h3.ticket-card__title - a(href='#!') Дрон с камерой + a(href=`#!`) Дрон с камерой p.ticket-card__price span.js-sum 32000 | ₽ @@ -109,14 +109,14 @@ block content li.tickets-list__item .ticket-card.ticket-card--color10 .ticket-card__img - img(src='/img/item10.jpg', srcset='/img/item10@2x.jpg 2x', alt='Изображение товара') + img(src=`/img/item10.jpg`, srcset=`/img/item10@2x.jpg 2x`, alt=`Изображение товара`) .ticket-card__info span.ticket-card__label ПРОДАМ .ticket-card__categories - a(href='#!') ЭЛЕКТРОНИКА + a(href=`#!`) ЭЛЕКТРОНИКА .ticket-card__header h3.ticket-card__title - a(href='#!') Мое старое кресло + a(href=`#!`) Мое старое кресло p.ticket-card__price span.js-sum 4000 | ₽ @@ -125,14 +125,14 @@ block content li.tickets-list__item .ticket-card.ticket-card--color06 .ticket-card__img - img(src='/img/item06.jpg', srcset='/img/item06@2x.jpg 2x', alt='Изображение товара') + img(src=`/img/item06.jpg`, srcset=`/img/item06@2x.jpg 2x`, alt=`Изображение товара`) .ticket-card__info span.ticket-card__label ПРОДАМ .ticket-card__categories - a(href='#!') ЭЛЕКТРОНИКА + a(href=`#!`) ЭЛЕКТРОНИКА .ticket-card__header h3.ticket-card__title - a(href='#!') Ableton + a(href=`#!`) Ableton p.ticket-card__price span.js-sum 88 000 | ₽ @@ -141,14 +141,14 @@ block content .tickets-list__pagination ul.pagination li - a.active(href='#!') 1 + a.active(href=`#!`) 1 li - a(href='#!') 2 + a(href=`#!`) 2 li - a(href='#!') 3 + a(href=`#!`) 3 li - a(href='#!') 4 + a(href=`#!`) 4 li - a(href='#!') 5 + a(href=`#!`) 5 li - a(href='#!') дальше + a(href=`#!`) дальше diff --git a/src/express/templates/entries/comments.pug b/src/express/templates/entries/comments.pug index 9ff2593..4d28938 100644 --- a/src/express/templates/entries/comments.pug +++ b/src/express/templates/entries/comments.pug @@ -4,75 +4,22 @@ block content section.comments .comments__wrapper h1.visually-hidden Страница комментариев - .comments__block - .comments__header - a.announce-card(href='#!') - h2.announce-card__title Ленд Ровер - span.announce-card__info - span.announce-card__price ₽ 900 000 - span.announce-card__type ПРОДАМ - ul.comments-list - li.js-card - .comment-card - .comment-card__header - a.comment-card__avatar.avatar(href='#!') - img(src='/img/avatar03.jpg', srcset='/img/avatar03@2x.jpg 2x', alt='Аватар пользователя') - p.comment-card__author Александр Бурый - .comment-card__content - p - | А что с прогоном автомобиля? Также вижу на фото зимнюю резину. А летняя идет ли впридачу? - button.comment-card__delete.js-delete(type='button') Удалить - li.js-card - .comment-card - .comment-card__header - a.comment-card__avatar.avatar(href='#!') - img(src='/img/avatar04.jpg', srcset='/img/avatar04@2x.jpg 2x', alt='Аватар пользователя') - p.comment-card__author Анатолий Хакимов - .comment-card__content - p Хочу прийти посмотреть на авто в среду. Мой телефон 89254455566. Готовы принять? - button.comment-card__delete.js-delete(type='button') Удалить - li.js-card - .comment-card - .comment-card__header - a.comment-card__avatar.avatar(href='#!') - img(src='/img/avatar02.jpg', srcset='/img/avatar02@2x.jpg 2x', alt='Аватар пользователя') - p.comment-card__author Георгий Шпиц - .comment-card__content - p Что это за рухлядь? Стыдно такое даже фотографировать, не то, что продавать. - button.comment-card__delete.js-delete(type='button') Удалить - .comments__block - .comments__header - a.announce-card(href='#!') - h2.announce-card__title Ableton - span.announce-card__info - span.announce-card__price ₽ 900 000 - span.announce-card__type ПРОДАМ - ul.comments-list - li.js-card - .comment-card - .comment-card__header - a.comment-card__avatar.avatar(href='#!') - img(src='/img/avatar03.jpg', srcset='/img/avatar03@2x.jpg 2x', alt='Аватар пользователя') - p.comment-card__author Александр Бурый - .comment-card__content - p - | А что с прогоном автомобиля? Также вижу на фото зимнюю резину. А летняя идет ли впридачу? - button.comment-card__delete.js-delete(type='button') Удалить - li.js-card - .comment-card - .comment-card__header - a.comment-card__avatar.avatar(href='#!') - img(src='/img/avatar04.jpg', srcset='/img/avatar04@2x.jpg 2x', alt='Аватар пользователя') - p.comment-card__author Анатолий Хакимов - .comment-card__content - p Хочу прийти посмотреть на авто в среду. Мой телефон 89254455566. Готовы принять? - button.comment-card__delete.js-delete(type='button') Удалить - li.js-card - .comment-card - .comment-card__header - a.comment-card__avatar.avatar(href='#!') - img(src='/img/avatar02.jpg', srcset='/img/avatar02@2x.jpg 2x', alt='Аватар пользователя') - p.comment-card__author Георгий Шпиц - .comment-card__content - p Что это за рухлядь? Стыдно такое даже фотографировать, не то, что продавать. - button.comment-card__delete.js-delete(type='button') Удалить + each offer in offers + .comments__block + .comments__header + a.announce-card(href=`#!`) + h2.announce-card__title #{offer.title} + span.announce-card__info + span.announce-card__price ₽ #{offer.outputPrice} + +ticketLabel(offer.type)(class=`announce-card__type`) + ul.comments-list + each comment in offer.comments + li.js-card + .comment-card + .comment-card__header + a.comment-card__avatar.avatar(href=`#!`) + img(src=`/img/avatar03.jpg`, srcset=`/img/avatar03@2x.jpg 2x`, alt=`Аватар пользователя`) + p.comment-card__author Александр Бурый + .comment-card__content + p #{comment.text} + button.comment-card__delete.js-delete(type=`button`) Удалить diff --git a/src/express/templates/entries/login.pug b/src/express/templates/entries/login.pug index b48eacb..54fbe45 100644 --- a/src/express/templates/entries/login.pug +++ b/src/express/templates/entries/login.pug @@ -2,16 +2,16 @@ extends ../layout block content section.login - form.login__form.form(action='#', method='post', enctype='multipart/form-data') + form.login__form.form(action=`#`, method=`post`, enctype=`multipart/form-data`) .login__title - a.login__link(href='/register') Регистрация + a.login__link(href=`/register`) Регистрация h2 Вход .form__field.login__field - input#user-email.js-field(type='email', name='user-email', required='') - label(for='user-email') Эл. почта + input#user-email.js-field(type=`email`, name=`user-email`, required=``) + label(for=`user-email`) Эл. почта span Обязательное поле .form__field.login__field - input#user-password.js-field(type='password', name='user-password', required='') - label(for='user-password') Пароль + input#user-password.js-field(type=`password`, name=`user-password`, required=``) + label(for=`user-password`) Пароль span Обязательное поле - button.login__button.btn.btn--medium.js-button(type='submit', disabled='') Войти + button.login__button.btn.btn--medium.js-button(type=`submit`, disabled=``) Войти diff --git a/src/express/templates/entries/main.pug b/src/express/templates/entries/main.pug index 6909a5f..ed383c2 100644 --- a/src/express/templates/entries/main.pug +++ b/src/express/templates/entries/main.pug @@ -8,136 +8,24 @@ block content .tickets-list__header p.tickets-list__title Самое свежее ul - li.tickets-list__item - .ticket-card.ticket-card--color01 - .ticket-card__img - img(src='/img/item01.jpg', srcset='/img/item01@2x.jpg 2x', alt='Изображение товара') - .ticket-card__info - span.ticket-card__label Куплю - .ticket-card__categories - a(href='#!') Дом - .ticket-card__header - h3.ticket-card__title - a(href='#!') Монстера - p.ticket-card__price - span.js-sum 1000 - | ₽ - .ticket-card__desc - p Куплю монстеру зеленую в хорошем зеленом состоянии, буду поливать... - li.tickets-list__item - .ticket-card.ticket-card--color02 - .ticket-card__img - img(src='/img/item02.jpg', srcset='/img/item02@2x.jpg 2x', alt='Изображение товара') - .ticket-card__info - span.ticket-card__label ПРОДАМ - .ticket-card__categories - a(href='#!') Дом - .ticket-card__header - h3.ticket-card__title - a(href='#!') Мое старое кресло - p.ticket-card__price - span.js-sum 4000 - | ₽ - .ticket-card__desc - p Продам свое старое кресло, чтобы сидеть и читать книги зимними... - li.tickets-list__item - .ticket-card.ticket-card--color03 - .ticket-card__img - img(src='/img/item03.jpg', srcset='/img/item03@2x.jpg 2x', alt='Изображение товара') - .ticket-card__info - span.ticket-card__label ПРОДАМ - .ticket-card__categories - a(href='#!') ЭЛЕКТРОНИКА - a(href='#!') Дом - .ticket-card__header - h3.ticket-card__title - a(href='#!') Дедушкины часы - p.ticket-card__price - span.js-sum 45 000 - | ₽ - .ticket-card__desc - p Продаю дедушкины часы в прекрасном состоянии, ходят до... - li.tickets-list__item - .ticket-card.ticket-card--color04 - .ticket-card__img - img(src='/img/item04.jpg', srcset='/img/item04@2x.jpg 2x', alt='Изображение товара') - .ticket-card__info - span.ticket-card__label Куплю - .ticket-card__categories - a(href='#!') Дом - .ticket-card__header - h3.ticket-card__title - a(href='#!') Кофеварка - p.ticket-card__price - span.js-sum 2000 - | ₽ - .ticket-card__desc - p Куплю вот такую итальянскую кофеварку, можно любой фирмы... - li.tickets-list__item - .ticket-card.ticket-card--color05 - .ticket-card__img - img(src='/img/item05.jpg', srcset='/img/item05@2x.jpg 2x', alt='Изображение товара') - .ticket-card__info - span.ticket-card__label ПРОДАМ - .ticket-card__categories - a(href='#!') Авто - a(href='#!') ЭЛЕКТРОНИКА - .ticket-card__header - h3.ticket-card__title - a(href='#!') Ленд Ровер - p.ticket-card__price - span.js-sum 900 000 - | ₽ - .ticket-card__desc - p Куплю монстеру зеленую в хорошем зеленом состоянии, буду поливать... - li.tickets-list__item - .ticket-card.ticket-card--color06 - .ticket-card__img - img(src='/img/item06.jpg', srcset='/img/item06@2x.jpg 2x', alt='Изображение товара') - .ticket-card__info - span.ticket-card__label ПРОДАМ - .ticket-card__categories - a(href='#!') ЭЛЕКТРОНИКА - .ticket-card__header - h3.ticket-card__title - a(href='#!') Ableton - p.ticket-card__price - span.js-sum 88 000 - | ₽ - .ticket-card__desc - p Продам свое старое кресло, чтобы сидеть и читать книги зимними... - li.tickets-list__item - .ticket-card.ticket-card--color07 - .ticket-card__img - img(src='/img/item07.jpg', srcset='/img/item07@2x.jpg 2x', alt='Изображение товара') - .ticket-card__info - span.ticket-card__label ПРОДАМ - .ticket-card__categories - a(href='#!') Спорт и отдых - .ticket-card__header - h3.ticket-card__title - a(href='#!') Доска - p.ticket-card__price - span.js-sum 55 000 - | ₽ - .ticket-card__desc - p Продаю дедушкины часы в прекрасном состоянии, ходят до... - li.tickets-list__item - .ticket-card.ticket-card--color08 - .ticket-card__img - img(src='/img/item08.jpg', srcset='/img/item08@2x.jpg 2x', alt='Изображение товара') - .ticket-card__info - span.ticket-card__label Куплю - .ticket-card__categories - a(href='#!') ЭЛЕКТРОНИКА - .ticket-card__header - h3.ticket-card__title - a(href='#!') Фотик Canon - p.ticket-card__price - span.js-sum 32 000 - | ₽ - .ticket-card__desc - p Куплю вот такую итальянскую кофеварку, можно любой фирмы... + each offer in offers + li.tickets-list__item + .ticket-card(class=`ticket-card--color${offer.colorIndex}`) + .ticket-card__img + img(src=`/img/${offer.picture}`, srcset=`/img/${offer.retinaPicture} 2x`, alt=offer.title) + .ticket-card__info + +ticketLabel(offer.type) + .ticket-card__categories + each category in offer.category + a(href=`#!`) #{category} + .ticket-card__header + h3.ticket-card__title + a(href=`#!`) #{offer.title} + p.ticket-card__price + span.js-sum #{offer.outputPrice} + |  ₽ + .ticket-card__desc + p #{offer.description} section.tickets-list h2.visually-hidden Самые обсуждаемые предложения .tickets-list__wrapper @@ -147,14 +35,14 @@ block content li.tickets-list__item .ticket-card.ticket-card--color09 .ticket-card__img - img(src='/img/item09.jpg', srcset='/img/item09@2x.jpg 2x', alt='Изображение товара') + img(src=`/img/item09.jpg`, srcset=`/img/item09@2x.jpg 2x`, alt=`Изображение товара`) .ticket-card__info span.ticket-card__label Куплю .ticket-card__categories - a(href='#!') Дом + a(href=`#!`) Дом .ticket-card__header h3.ticket-card__title - a(href='#!') Монстера + a(href=`#!`) Монстера p.ticket-card__price span.js-sum 1000 | ₽ @@ -163,14 +51,14 @@ block content li.tickets-list__item .ticket-card.ticket-card--color10 .ticket-card__img - img(src='/img/item10.jpg', srcset='/img/item10@2x.jpg 2x', alt='Изображение товара') + img(src=`/img/item10.jpg`, srcset=`/img/item10@2x.jpg 2x`, alt=`Изображение товара`) .ticket-card__info span.ticket-card__label ПРОДАМ .ticket-card__categories - a(href='#!') Дом + a(href=`#!`) Дом .ticket-card__header h3.ticket-card__title - a(href='#!') Мое старое кресло + a(href=`#!`) Мое старое кресло p.ticket-card__price span.js-sum 4000 | ₽ @@ -179,14 +67,14 @@ block content li.tickets-list__item .ticket-card.ticket-card--color11 .ticket-card__img - img(src='/img/item11.jpg', srcset='/img/item11@2x.jpg 2x', alt='Изображение товара') + img(src=`/img/item11.jpg`, srcset=`/img/item11@2x.jpg 2x`, alt=`Изображение товара`) .ticket-card__info span.ticket-card__label ПРОДАМ .ticket-card__categories - a(href='#!') ЭЛЕКТРОНИКА + a(href=`#!`) ЭЛЕКТРОНИКА .ticket-card__header h3.ticket-card__title - a(href='#!') Дедушкины часы + a(href=`#!`) Дедушкины часы p.ticket-card__price span.js-sum 45 000 | ₽ @@ -195,14 +83,14 @@ block content li.tickets-list__item .ticket-card.ticket-card--color04 .ticket-card__img - img(src='/img/item04.jpg', srcset='/img/item04@2x.jpg 2x', alt='Изображение товара') + img(src=`/img/item04.jpg`, srcset=`/img/item04@2x.jpg 2x`, alt=`Изображение товара`) .ticket-card__info span.ticket-card__label Куплю .ticket-card__categories - a(href='#!') Дом + a(href=`#!`) Дом .ticket-card__header h3.ticket-card__title - a(href='#!') Кофеварка + a(href=`#!`) Кофеварка p.ticket-card__price span.js-sum 2000 | ₽ diff --git a/src/express/templates/entries/my-tickets.pug b/src/express/templates/entries/my-tickets.pug index ded6c6b..9f911db 100644 --- a/src/express/templates/entries/my-tickets.pug +++ b/src/express/templates/entries/my-tickets.pug @@ -5,81 +5,24 @@ block content h2.visually-hidden Самые новые предложения .tickets-list__wrapper .tickets-list__header - a.tickets-list__btn.btn.btn--big(href='/offers/add') + a.tickets-list__btn.btn.btn--big(href=`/offers/add`) span Новая публикация ul - li.tickets-list__item.js-card - .ticket-card.ticket-card--color06 - .ticket-card__img - img(src='/img/item06.jpg', srcset='/img/item06@2x.jpg 2x', alt='Изображение товара') - .ticket-card__info - span.ticket-card__label ПРОДАМ - .ticket-card__categories - a(href='#!') ЭЛЕКТРОНИКА - .ticket-card__header - h3.ticket-card__title - a(href='#!') Ableton - p.ticket-card__price - span.js-sum 88 000 - | ₽ - button.ticket-card__del.js-delete(type='button') Удалить - li.tickets-list__item.js-card - .ticket-card.ticket-card--color10 - .ticket-card__img - img(src='/img/item10.jpg', srcset='/img/item10@2x.jpg 2x', alt='Изображение товара') - .ticket-card__info - span.ticket-card__label ПРОДАМ - .ticket-card__categories - a(href='#!') Дом - .ticket-card__header - h3.ticket-card__title - a(href='#!') Мое старое кресло - p.ticket-card__price - span.js-sum 4000 - | ₽ - button.ticket-card__del.js-delete(type='button') Удалить - li.tickets-list__item.js-card - .ticket-card.ticket-card--color04 - .ticket-card__img - img(src='/img/item04.jpg', srcset='/img/item04@2x.jpg 2x', alt='Изображение товара') - .ticket-card__info - span.ticket-card__label Куплю - .ticket-card__categories - a(href='#!') Дом - .ticket-card__header - h3.ticket-card__title - a(href='#!') Кофеварка - p.ticket-card__price - span.js-sum 2000 - | ₽ - button.ticket-card__del.js-delete(type='button') Удалить - li.tickets-list__item.js-card - .ticket-card.ticket-card--color08 - .ticket-card__img - img(src='/img/item08.jpg', srcset='/img/item08@2x.jpg 2x', alt='Изображение товара') - .ticket-card__info - span.ticket-card__label Куплю - .ticket-card__categories - a(href='#!') ЭЛЕКТРОНИКА - .ticket-card__header - h3.ticket-card__title - a(href='#!') Фотик Canon - p.ticket-card__price - span.js-sum 32 000 - | ₽ - button.ticket-card__del.js-delete(type='button') Удалить - li.tickets-list__item.js-card - .ticket-card.ticket-card--color01 - .ticket-card__img - img(src='/img/item01.jpg', srcset='/img/item01@2x.jpg 2x', alt='Изображение товара') - .ticket-card__info - span.ticket-card__label Куплю - .ticket-card__categories - a(href='#!') Дом - .ticket-card__header - h3.ticket-card__title - a(href='#!') Монстера - p.ticket-card__price - span.js-sum 1000 - | ₽ - button.ticket-card__del.js-delete(type='button') Удалить + each offer in offers + li.tickets-list__item + .ticket-card(class=`ticket-card--color${offer.colorIndex}`) + .ticket-card__img + img(src=`/img/${offer.picture}`, srcset=`/img/${offer.retinaPicture} 2x`, alt=offer.title) + .ticket-card__info + +ticketLabel(offer.type) + .ticket-card__categories + each category in offer.category + a(href=`#!`) #{category} + .ticket-card__header + h3.ticket-card__title + a(href=`#!`) #{offer.title} + p.ticket-card__price + span.js-sum #{offer.outputPrice} + |  ₽ + .ticket-card__desc + p #{offer.description} diff --git a/src/express/templates/entries/new-ticket.pug b/src/express/templates/entries/new-ticket.pug index 5bfd5b2..8f51a78 100644 --- a/src/express/templates/entries/new-ticket.pug +++ b/src/express/templates/entries/new-ticket.pug @@ -5,41 +5,39 @@ block content .ticket-form__wrapper h1.ticket-form__title Новая публикация .ticket-form__tile - form.ticket-form__form.form(action='#', method='post', enctype='multipart/form-data', autocomplete='off') + form.ticket-form__form.form(action=`/offers/add`, method=`post`, enctype=`multipart/form-data`, autocomplete=`off`) .ticket-form__avatar-container.js-preview-container .ticket-form__avatar.js-preview .ticket-form__field-avatar - input#avatar.visually-hidden.js-file-field(type='file', name='avatar') - label(for='avatar') + input#avatar.visually-hidden.js-file-field(type=`file`, name=`avatar`) + label(for=`avatar`) span.ticket-form__text-upload Загрузить фото… span.ticket-form__text-another Загрузить другое фото… .ticket-form__content .ticket-form__row .form__field - input#ticket-name.js-field(type='text', name='ticket-name', required='') - label(for='ticket-name') Название + input#ticket-name.js-field(type=`text`, name=`ticket-name`, required=``) + label(for=`ticket-name`) Название span Обязательное поле .ticket-form__row .form__field - textarea#comment-field.js-field(name='comment', cols='30', rows='10', maxlength='400', minlength='50') - label(for='comment-field') Описание + textarea#comment-field.js-field(name=`comment`, cols=`30`, rows=`10`, maxlength=`400`, minlength=`50`) + label(for=`comment-field`) Описание span Обязательное поле .ticket-form__row - select#category-field.form__select.js-multiple-select(name='category', data-label='Выбрать категорию публикации') - option(value='1') Дом - option(value='2') Спорт и отдых - option(value='3') Авто - option(value='4') Электроника + select#category-field.form__select.js-multiple-select(name=`category`, data-label=`Выбрать категорию публикации`) + each category in categories + option(value=category) #{category} .ticket-form__row .form__field.form__field--price - input#price-field.js-field.js-price(type='number', name='price', min='1', required='') - label(for='price-field') Цена + input#price-field.js-field.js-price(type=`number`, name=`price`, min=`1`, required=``) + label(for=`price-field`) Цена span Обязательное поле .form__switch.switch .switch__item - input#buy-field.visually-hidden(type='radio', name='action', value='buy') - label.switch__button(for='buy-field') Куплю + input#offer-field.visually-hidden(type=`radio`, name=`action`, value=`offer`) + label.switch__button(for=`offer-field`) Куплю .switch__item - input#sell-field.visually-hidden(type='radio', name='action', value='sell') - label.switch__button(for='sell-field') Продам - button.form__button.btn.btn--medium.js-button(type='submit', disabled='') Опубликовать + input#sale-field.visually-hidden(type=`radio`, name=`action`, value=`sale`) + label.switch__button(for=`sale-field`) Продам + button.form__button.btn.btn--medium.js-button(type=`submit`, disabled=``) Опубликовать diff --git a/src/express/templates/entries/search-result.pug b/src/express/templates/entries/search-result.pug index ec0105e..5953a05 100644 --- a/src/express/templates/entries/search-result.pug +++ b/src/express/templates/entries/search-result.pug @@ -4,60 +4,53 @@ block content section.search-results h1.visually-hidden Результаты поиска .search-results__wrapper - p.search-results__label - | Найдено - span.js-results 2 публикации - ul.search-results__list - li.search-results__item - .ticket-card.ticket-card--color05 - .ticket-card__img - img(src='/img/item05.jpg', srcset='/img/item05@2x.jpg 2x', alt='Изображение товара') - .ticket-card__info - span.ticket-card__label ПРОДАМ - .ticket-card__categories - a(href='#!') Авто - .ticket-card__header - h3.ticket-card__title - a(href='#!') Ленд Ровер - p.ticket-card__price - span.js-sum 900 000 - | ₽ - .ticket-card__desc - p Куплю монстеру зеленую в хорошем зеленом состоянии, буду поливать... - li.search-results__item - .ticket-card.ticket-card--color16 - .ticket-card__img - img(src='/img/item16.jpg', srcset='/img/item16@2x.jpg 2x', alt='Изображение товара') - .ticket-card__info - span.ticket-card__label ПРОДАМ - .ticket-card__categories - a(href='#!') Авто - .ticket-card__header - h3.ticket-card__title - a(href='#!') Хонда - p.ticket-card__price - span.js-sum 100 000 - | ₽ - .ticket-card__desc - p Куплю монстеру зеленую в хорошем зеленом состоянии, буду поливать... + if results.length + p.search-results__label + | #{findWord} + span.js-results #{results.length} #{pubWord} + ul.search-results__list + each offer in results + li.search-results__item + .ticket-card(class=`ticket-card--color${colorIndex}`) + .ticket-card__img + img(src=`/img/${offer.picture}`, srcset=`/img/${offer.retinaPicture} 2x`, alt=offer.title) + .ticket-card__info + +ticketLabel(offer.type) + .ticket-card__categories + each category in offer.category + a(href=`#!`) #{category} + .ticket-card__header + h3.ticket-card__title + a(href=`#!`) #{offer.title} + p.ticket-card__price + span.js-sum #{offer.outputPrice} + |  ₽ + .ticket-card__desc + p #{offer.description} + else + .search-results__message + p + | Не найдено + br + | ни одной публикации section.tickets-list h2.visually-hidden Самые новые предложения .tickets-list__wrapper .tickets-list__header p.tickets-list__title Самое свежее - a.tickets-list__link(href='#!') Еще 25 + a.tickets-list__link(href=`#!`) Еще 25 ul li.tickets-list__item .ticket-card.ticket-card--color01 .ticket-card__img - img(src='/img/item01.jpg', srcset='/img/item01@2x.jpg 2x', alt='Изображение товара') + img(src=`/img/item01.jpg`, srcset=`/img/item01@2x.jpg 2x`, alt=`Изображение товара`) .ticket-card__info span.ticket-card__label Куплю .ticket-card__categories - a(href='#!') Дом + a(href=`#!`) Дом .ticket-card__header h3.ticket-card__title - a(href='#!') Монстера + a(href=`#!`) Монстера p.ticket-card__price span.js-sum 1000 | ₽ @@ -66,14 +59,14 @@ block content li.tickets-list__item .ticket-card.ticket-card--color02 .ticket-card__img - img(src='/img/item02.jpg', srcset='/img/item02@2x.jpg 2x', alt='Изображение товара') + img(src=`/img/item02.jpg`, srcset=`/img/item02@2x.jpg 2x`, alt=`Изображение товара`) .ticket-card__info span.ticket-card__label ПРОДАМ .ticket-card__categories - a(href='#!') Дом + a(href=`#!`) Дом .ticket-card__header h3.ticket-card__title - a(href='#!') Мое старое кресло + a(href=`#!`) Мое старое кресло p.ticket-card__price span.js-sum 4000 | ₽ @@ -82,15 +75,15 @@ block content li.tickets-list__item .ticket-card.ticket-card--color03 .ticket-card__img - img(src='/img/item03.jpg', srcset='/img/item03@2x.jpg 2x', alt='Изображение товара') + img(src=`/img/item03.jpg`, srcset=`/img/item03@2x.jpg 2x`, alt=`Изображение товара`) .ticket-card__info span.ticket-card__label ПРОДАМ .ticket-card__categories - a(href='#!') ЭЛЕКТРОНИКА - a(href='#!') Дом + a(href=`#!`) ЭЛЕКТРОНИКА + a(href=`#!`) Дом .ticket-card__header h3.ticket-card__title - a(href='#!') Дедушкины часы + a(href=`#!`) Дедушкины часы p.ticket-card__price span.js-sum 45 000 | ₽ @@ -99,14 +92,14 @@ block content li.tickets-list__item .ticket-card.ticket-card--color04 .ticket-card__img - img(src='/img/item04.jpg', srcset='/img/item04@2x.jpg 2x', alt='Изображение товара') + img(src=`/img/item04.jpg`, srcset=`/img/item04@2x.jpg 2x`, alt=`Изображение товара`) .ticket-card__info span.ticket-card__label Куплю .ticket-card__categories - a(href='#!') Дом + a(href=`#!`) Дом .ticket-card__header h3.ticket-card__title - a(href='#!') Кофеварка + a(href=`#!`) Кофеварка p.ticket-card__price span.js-sum 2000 | ₽ @@ -115,15 +108,15 @@ block content li.tickets-list__item .ticket-card.ticket-card--color05 .ticket-card__img - img(src='/img/item05.jpg', srcset='/img/item05@2x.jpg 2x', alt='Изображение товара') + img(src=`/img/item05.jpg`, srcset=`/img/item05@2x.jpg 2x`, alt=`Изображение товара`) .ticket-card__info span.ticket-card__label ПРОДАМ .ticket-card__categories - a(href='#!') Авто - a(href='#!') ЭЛЕКТРОНИКА + a(href=`#!`) Авто + a(href=`#!`) ЭЛЕКТРОНИКА .ticket-card__header h3.ticket-card__title - a(href='#!') Ленд Ровер + a(href=`#!`) Ленд Ровер p.ticket-card__price span.js-sum 900 000 | ₽ @@ -132,14 +125,14 @@ block content li.tickets-list__item .ticket-card.ticket-card--color06 .ticket-card__img - img(src='/img/item06.jpg', srcset='/img/item06@2x.jpg 2x', alt='Изображение товара') + img(src=`/img/item06.jpg`, srcset=`/img/item06@2x.jpg 2x`, alt=`Изображение товара`) .ticket-card__info span.ticket-card__label ПРОДАМ .ticket-card__categories - a(href='#!') ЭЛЕКТРОНИКА + a(href=`#!`) ЭЛЕКТРОНИКА .ticket-card__header h3.ticket-card__title - a(href='#!') Ableton + a(href=`#!`) Ableton p.ticket-card__price span.js-sum 88 000 | ₽ @@ -148,14 +141,14 @@ block content li.tickets-list__item .ticket-card.ticket-card--color07 .ticket-card__img - img(src='/img/item07.jpg', srcset='/img/item07@2x.jpg 2x', alt='Изображение товара') + img(src=`/img/item07.jpg`, srcset=`/img/item07@2x.jpg 2x`, alt=`Изображение товара`) .ticket-card__info span.ticket-card__label ПРОДАМ .ticket-card__categories - a(href='#!') Спорт и отдых + a(href=`#!`) Спорт и отдых .ticket-card__header h3.ticket-card__title - a(href='#!') Доска + a(href=`#!`) Доска p.ticket-card__price span.js-sum 55 000 | ₽ @@ -164,14 +157,14 @@ block content li.tickets-list__item .ticket-card.ticket-card--color08 .ticket-card__img - img(src='/img/item08.jpg', srcset='/img/item08@2x.jpg 2x', alt='Изображение товара') + img(src=`/img/item08.jpg`, srcset=`/img/item08@2x.jpg 2x`, alt=`Изображение товара`) .ticket-card__info span.ticket-card__label Куплю .ticket-card__categories - a(href='#!') ЭЛЕКТРОНИКА + a(href=`#!`) ЭЛЕКТРОНИКА .ticket-card__header h3.ticket-card__title - a(href='#!') Фотик Canon + a(href=`#!`) Фотик Canon p.ticket-card__price span.js-sum 32 000 | ₽ diff --git a/src/express/templates/entries/sign-up.pug b/src/express/templates/entries/sign-up.pug index 868685d..9153b1e 100644 --- a/src/express/templates/entries/sign-up.pug +++ b/src/express/templates/entries/sign-up.pug @@ -2,31 +2,31 @@ extends ../layout block content section.sign-up - form.sign-up__form.form(action='#', method='post', enctype='multipart/form-data', autocomplete='off') + form.sign-up__form.form(action=`#`, method=`post`, enctype=`multipart/form-data`, autocomplete=`off`) .sign-up__title h2 Регистрация - a.sign-up__link(href='/login') Вход + a.sign-up__link(href=`/login`) Вход .sign-up__avatar-container.js-preview-container .sign-up__avatar.js-preview .sign-up__field-avatar - input#avatar.visually-hidden.js-file-field(type='file', name='avatar') - label(for='avatar') + input#avatar.visually-hidden.js-file-field(type=`file`, name=`avatar`) + label(for=`avatar`) span.sign-up__text-upload Загрузить аватар… span.sign-up__text-another Загрузить другой аватар… .form__field.sign-up__field - input#user-name.js-field(type='text', name='user-name', required='') - label(for='user-name') Имя и фамилия + input#user-name.js-field(type=`text`, name=`user-name`, required=``) + label(for=`user-name`) Имя и фамилия span Обязательное поле .form__field.sign-up__field - input#user-email.js-field(type='email', name='user-email', required='') - label(for='user-email') Эл. почта + input#user-email.js-field(type=`email`, name=`user-email`, required=``) + label(for=`user-email`) Эл. почта span Обязательное поле .form__field.sign-up__field - input#user-password.js-field(type='password', name='user-password', required='') - label(for='user-password') Пароль + input#user-password.js-field(type=`password`, name=`user-password`, required=``) + label(for=`user-password`) Пароль span Обязательное поле .form__field.sign-up__field - input#user-password-again.js-field(type='password', name='user-password-again', required='') - label(for='user-password-again') Пароль еще раз + input#user-password-again.js-field(type=`password`, name=`user-password-again`, required=``) + label(for=`user-password-again`) Пароль еще раз span Обязательное поле - button.sign-up__button.btn.btn--medium.js-button(type='submit', disabled='') Создать аккаунт + button.sign-up__button.btn.btn--medium.js-button(type=`submit`, disabled=``) Создать аккаунт diff --git a/src/express/templates/entries/ticket-edit.pug b/src/express/templates/entries/ticket-edit.pug index db93436..16125ea 100644 --- a/src/express/templates/entries/ticket-edit.pug +++ b/src/express/templates/entries/ticket-edit.pug @@ -5,43 +5,41 @@ block content .ticket-form__wrapper h1.ticket-form__title Редактировать публикацию .ticket-form__tile - form.ticket-form__form.form(action='#', method='post', enctype='multipart/form-data', autocomplete='off') + form.ticket-form__form.form(action=`#`, method=`post`, enctype=`multipart/form-data`, autocomplete=`off`) .ticket-form__avatar-container.js-preview-container.uploaded .ticket-form__avatar.js-preview - img(src='/img/item02.jpg', srcset='/img/item02@2x.jpg 2x', alt='') + img(src=`/img/${offer.picture}`, srcset=`/img/${offer.retinaPicture} 2x`, alt=offer.title) .ticket-form__field-avatar - input#avatar.visually-hidden.js-file-field(type='file', name='avatar') - label(for='avatar') + input#avatar.visually-hidden.js-file-field(type=`file`, name=`avatar`) + label(for=`avatar`) span.ticket-form__text-upload Загрузить фото… span.ticket-form__text-another Загрузить другое фото… .ticket-form__content .ticket-form__row .form__field - input#ticket-name.js-field(type='text', name='ticket-name', value='Мое старое кресло', required='') - label(for='ticket-name') Название + input#ticket-name.js-field(type=`text`, name=`ticket-name`, value=offer.title, required=``) + label(for=`ticket-name`) Название span Обязательное поле .ticket-form__row .form__field - textarea#comment-field.js-field(name='comment', cols='30', rows='10', maxlength='400', minlength='50') - | Продам свое старое кресло, чтобы сидеть и читать книги зимними вечерами. Ножки мягкие, мой пол не царапают. - label(for='comment-field') Описание + textarea#comment-field.js-field(name=`comment`, cols=`30`, rows=`10`, maxlength=`400`, minlength=`50`) + | #{offer.description} + label(for=`comment-field`) Описание span Обязательное поле .ticket-form__row - select#category-field.form__select.js-multiple-select(name='category', data-label='Выбрать категорию публикации', multiple='') - option(value='1', selected='') Дом - option(value='2', selected='') Спорт и отдых - option(value='3') Авто - option(value='4') Электроника + select#category-field.form__select.js-multiple-select(name=`category`, data-label=`Выбрать категорию публикации`, multiple=``) + each category, index in categories + option(value=index selected=offer.category.includes(category)) #{category} .ticket-form__row .form__field.form__field--price - input#price-field.js-field.js-price(type='number', name='price', min='1', value='4000', required='') - label(for='price-field') Цена + input#price-field.js-field.js-price(type=`number`, name=`price`, min=`1`, value=offer.sum, required=``) + label(for=`price-field`) Цена span Обязательное поле .form__switch.switch .switch__item - input#buy-field.visually-hidden(type='radio', name='action', value='buy', checked='') - label.switch__button(for='buy-field') Куплю + input#offer-field.visually-hidden(type=`radio`, name=`action`, value=`offer`, checked=(offer.type === 'offer')) + label.switch__button(for=`offer-field`) Куплю .switch__item - input#sell-field.visually-hidden(type='radio', name='action', value='sell') - label.switch__button(for='sell-field') Продам - button.form__button.btn.btn--medium.js-button(type='submit') Сохранить + input#sale-field.visually-hidden(type=`radio`, name=`action`, value=`sale`, checked=(offer.type === 'sale')) + label.switch__button(for=`sale-field`) Продам + button.form__button.btn.btn--medium.js-button(type=`submit`) Сохранить diff --git a/src/express/templates/entries/ticket.pug b/src/express/templates/entries/ticket.pug index a26f82f..bff52fd 100644 --- a/src/express/templates/entries/ticket.pug +++ b/src/express/templates/entries/ticket.pug @@ -6,7 +6,7 @@ block content h1.visually-hidden Карточка объявления .ticket__content .ticket__img - img(src='/img/ticket.jpg', srcset='/img/ticket@2x.jpg 2x', alt='Изображение товара') + img(src=`/img/ticket.jpg`, srcset=`/img/ticket@2x.jpg 2x`, alt=`Изображение товара`) .ticket__info h2.ticket__title Мое старое кресло .ticket__header @@ -23,50 +23,50 @@ block content span 20 ноября 2019 p b Автор: - a(href='#!') Денис Шкатулкин + a(href=`#!`) Денис Шкатулкин p b Контакты: - a(href='mailto:shkatulkin@ya.ru') shkatulkin@ya.ru + a(href=`mailto:shkatulkin@ya.ru`) shkatulkin@ya.ru ul.ticket__tags li - a.category-tile.category-tile--small(href='#!') + a.category-tile.category-tile--small(href=`#!`) span.category-tile__image - img(src='/img/cat.jpg', srcset='/img/cat@2x.jpg 2x', alt='Иконка категории') + img(src=`/img/cat.jpg`, srcset=`/img/cat@2x.jpg 2x`, alt=`Иконка категории`) span.category-tile__label Дом li - a.category-tile.category-tile--small(href='#!') + a.category-tile.category-tile--small(href=`#!`) span.category-tile__image - img(src='/img/cat04.jpg', srcset='/img/cat04@2x.jpg 2x', alt='Иконка категории') + img(src=`/img/cat04.jpg`, srcset=`/img/cat04@2x.jpg 2x`, alt=`Иконка категории`) span.category-tile__label Спорт и отдых .ticket__comments h2.ticket__subtitle Коментарии .ticket__comment-form - form.form.comment-form(action='#', method='post') + form.form.comment-form(action=`#`, method=`post`) .comment-form__header - a.comment-form__avatar.avatar(href='#!') - img(src='/img/avatar.jpg', srcset='/img/avatar@2x.jpg 2x', alt='Аватар пользователя') + a.comment-form__avatar.avatar(href=`#!`) + img(src=`/img/avatar.jpg`, srcset=`/img/avatar@2x.jpg 2x`, alt=`Аватар пользователя`) p.comment-form__author Вам слово .comment-form__field .form__field - textarea#comment-field.js-field(name='comment', cols='30', rows='10', maxlength='400', minlength='50') Нормальное вообще кресло! А как насч - label(for='comment-field') Текст комментария + textarea#comment-field.js-field(name=`comment`, cols=`30`, rows=`10`, maxlength=`400`, minlength=`50`) Нормальное вообще кресло! А как насч + label(for=`comment-field`) Текст комментария span Обязательное поле - button.comment-form__button.btn.btn--white.js-button(type='submit', disabled='') Отправить + button.comment-form__button.btn.btn--white.js-button(type=`submit`, disabled=``) Отправить .ticket__comments-list ul.comments-list li .comment-card .comment-card__header - a.comment-card__avatar.avatar(href='#!') - img(src='/img/avatar02.jpg', srcset='/img/avatar02@2x.jpg 2x', alt='Аватар пользователя') + a.comment-card__avatar.avatar(href=`#!`) + img(src=`/img/avatar02.jpg`, srcset=`/img/avatar02@2x.jpg 2x`, alt=`Аватар пользователя`) p.comment-card__author Георгий Шпиц .comment-card__content p Что это за рухлядь? Стыдно такое даже фотографировать, не то, что продавать. li .comment-card .comment-card__header - a.comment-card__avatar.avatar(href='#!') - img(src='/img/avatar03.jpg', srcset='/img/avatar03@2x.jpg 2x', alt='Аватар пользователя') + a.comment-card__avatar.avatar(href=`#!`) + img(src=`/img/avatar03.jpg`, srcset=`/img/avatar03@2x.jpg 2x`, alt=`Аватар пользователя`) p.comment-card__author Александр Бурый .comment-card__content p diff --git a/src/express/templates/layout.pug b/src/express/templates/layout.pug index 4dd1613..a7d2090 100644 --- a/src/express/templates/layout.pug +++ b/src/express/templates/layout.pug @@ -1,20 +1,22 @@ block constants doctype html -html(class=htmlClass||null, lang='ru') +html(class=htmlClass||null, lang=`ru`) head - meta(charset='UTF-8') - meta(name='description', content='Доска объявлений — современный веб-сайт, упрощающий продажу или покупку абсолютно любых вещей.') - meta(name='viewport', content='width=device-width, initial-scale=1.0') - meta(http-equiv='X-UA-Compatible', content='ie=edge') + meta(charset=`UTF-8`) + meta(name=`description`, content=`Доска объявлений — современный веб-сайт, упрощающий продажу или покупку абсолютно любых вещей.`) + meta(name=`viewport`, content=`width=device-width, initial-scale=1.0`) + meta(http-equiv=`X-UA-Compatible`, content=`ie=edge`) title Куплю. Продам - link(rel='stylesheet', href='/css/style.min.css') + link(rel=`stylesheet`, href=`/css/style.min.css`) body(class=bodyClass||null) + mixin ticketLabel(type) + span(class=attributes.class || `ticket-card__label`) #{type === `offer` ? `Куплю` : `Продам`} block header include partials/header - main(class=typeof contentClass === `undefined` ? 'page-content' : contentClass) + main(class=typeof contentClass === `undefined` ? `page-content` : contentClass) block content block footer include partials/footer - script(src='/js/vendor.js') - script(src='/js/main.js') + script(src=`/js/vendor.js`) + script(src=`/js/main.js`) diff --git a/src/express/templates/partials/categories.pug b/src/express/templates/partials/categories.pug index e16fac0..fa35ea2 100644 --- a/src/express/templates/partials/categories.pug +++ b/src/express/templates/partials/categories.pug @@ -2,44 +2,44 @@ section.categories-list h1.visually-hidden Сервис объявлений "Куплю - продам" ul.categories-list__wrapper li.categories-list__item - a.category-tile.category-tile--default(href='#!') + a.category-tile.category-tile--default(href=`#!`) span.category-tile__image - img(src='/img/cat.jpg', srcset='/img/cat@2x.jpg 2x', alt='Иконка категории') + img(src=`/img/cat.jpg`, srcset=`/img/cat@2x.jpg 2x`, alt=`Иконка категории`) span.category-tile__label | Дом span.category-tile__qty.js-qty 81 li.categories-list__item - a.category-tile.category-tile--default(href='#!') + a.category-tile.category-tile--default(href=`#!`) span.category-tile__image - img(src='/img/cat02.jpg', srcset='/img/cat02@2x.jpg 2x', alt='Иконка категории') + img(src=`/img/cat02.jpg`, srcset=`/img/cat02@2x.jpg 2x`, alt=`Иконка категории`) span.category-tile__label | Электроника span.category-tile__qty.js-qty 62 li.categories-list__item - a.category-tile.category-tile--default(href='#!') + a.category-tile.category-tile--default(href=`#!`) span.category-tile__image - img(src='/img/cat03.jpg', srcset='/img/cat03@2x.jpg 2x', alt='Иконка категории') + img(src=`/img/cat03.jpg`, srcset=`/img/cat03@2x.jpg 2x`, alt=`Иконка категории`) span.category-tile__label | Одежда span.category-tile__qty.js-qty 106 li.categories-list__item - a.category-tile.category-tile--default(href='#!') + a.category-tile.category-tile--default(href=`#!`) span.category-tile__image - img(src='/img/cat04.jpg', srcset='/img/cat04@2x.jpg 2x', alt='Иконка категории') + img(src=`/img/cat04.jpg`, srcset=`/img/cat04@2x.jpg 2x`, alt=`Иконка категории`) span.category-tile__label | Спорт/отдых span.category-tile__qty.js-qty 86 li.categories-list__item - a.category-tile.category-tile--default(href='#!') + a.category-tile.category-tile--default(href=`#!`) span.category-tile__image - img(src='/img/cat05.jpg', srcset='/img/cat05@2x.jpg 2x', alt='Иконка категории') + img(src=`/img/cat05.jpg`, srcset=`/img/cat05@2x.jpg 2x`, alt=`Иконка категории`) span.category-tile__label | Авто span.category-tile__qty.js-qty 34 li.categories-list__item - a.category-tile.category-tile--default(href='#!') + a.category-tile.category-tile--default(href=`#!`) span.category-tile__image - img(src='/img/cat06.jpg', srcset='/img/cat06@2x.jpg 2x', alt='Иконка категории') + img(src=`/img/cat06.jpg`, srcset=`/img/cat06@2x.jpg 2x`, alt=`Иконка категории`) span.category-tile__label | Книги span.category-tile__qty.js-qty 92 diff --git a/src/express/templates/partials/error-page-content.pug b/src/express/templates/partials/error-page-content.pug index 352194d..1fbb24f 100644 --- a/src/express/templates/partials/error-page-content.pug +++ b/src/express/templates/partials/error-page-content.pug @@ -1,13 +1,13 @@ ul.error__list li.error__item - a(href='/register') Вход и регистрация + a(href=`/register`) Вход и регистрация li.error__item - a(href='/offers/add') Новая публикация + a(href=`/offers/add`) Новая публикация li.error__item - a(href='/') Главная страница -form.error__search.search.search--small(method='get', action='/search', autocomplete='off') - input(type='search', name='search', placeholder='Поиск', aria-label='Поиск') + a(href=`/`) Главная страница +form.error__search.search.search--small(method=`get`, action=`/search`, autocomplete=`off`) + input(type=`search`, name=`search`, placeholder=`Поиск`, aria-label=`Поиск`) .search__icon .search__close-btn -a.error__logo.logo(href='/') - img(src='/img/logo.svg', alt='Логотип Куплю Продам', width='179', height='34') +a.error__logo.logo(href=`/`) + img(src=`/img/logo.svg`, alt=`Логотип Куплю Продам`, width=`179`, height=`34`) diff --git a/src/express/templates/partials/footer.pug b/src/express/templates/partials/footer.pug index 41b10a0..4817d3d 100644 --- a/src/express/templates/partials/footer.pug +++ b/src/express/templates/partials/footer.pug @@ -1,16 +1,16 @@ footer.page-footer .page-footer__wrapper .page-footer__col - a.page-footer__logo-academy(href='#!', aria-label='Ссылка на сайт HTML-Академии') - svg(width='132', height='46') - use(xlink:href='/img/sprite_auto.svg#logo-htmlac') + a.page-footer__logo-academy(href=`#!`, aria-label=`Ссылка на сайт HTML-Академии`) + svg(width=`132`, height=`46`) + use(xlink:href=`/img/sprite_auto.svg#logo-htmlac`) p.page-footer__copyright © 2019 Проект Академии .page-footer__col - a.page-footer__logo.logo(href='#!') - img(src='/img/logo.svg', alt='Логотип Куплю Продам', width='179', height='35') + a.page-footer__logo.logo(href=`#!`) + img(src=`/img/logo.svg`, alt=`Логотип Куплю Продам`, width=`179`, height=`35`) .page-footer__col ul.page-footer__nav li - a(href='/register') Вход и регистрация + a(href=`/register`) Вход и регистрация li - a(href='/offers/add') Создать объявление + a(href=`/offers/add`) Создать объявление diff --git a/src/express/templates/partials/header.pug b/src/express/templates/partials/header.pug index f6a8d17..028e4a7 100644 --- a/src/express/templates/partials/header.pug +++ b/src/express/templates/partials/header.pug @@ -1,17 +1,17 @@ header.header.header--logged .header__wrapper - a.header__logo.logo(href='/') - img(src='/img/logo.svg', alt='Логотип Куплю Продам', width='179', height='34') + a.header__logo.logo(href=`/`) + img(src=`/img/logo.svg`, alt=`Логотип Куплю Продам`, width=`179`, height=`34`) nav.header__user-menu ul.header__list li.header__item - a(href='/my') Публикации + a(href=`/my`) Публикации li.header__item - a(href='/my/comments') Комментарии - form.search(method='get', action='/search', autocomplete='off') - input(type='search', name='search', placeholder='Поиск', aria-label='Поиск') + a(href=`/my/comments`) Комментарии + form.search(method=`get`, action=`/search`, autocomplete=`off`) + input(type=`search`, name=`search`, placeholder=`Поиск`, aria-label=`Поиск`) .search__icon .search__close-btn - a.header__avatar.avatar(href='#!') - img(src='/img/avatar.jpg', srcset='/img/avatar@2x.jpg 2x', alt='Аватар пользователя') - a.header__input(href='/login') Вход и регистрация + a.header__avatar.avatar(href=`#!`) + img(src=`/img/avatar.jpg`, srcset=`/img/avatar@2x.jpg 2x`, alt=`Аватар пользователя`) + a.header__input(href=`/login`) Вход и регистрация diff --git a/src/express/upload/img/.gitkeep b/src/express/upload/img/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/service/api/category.e2e.test.js b/src/service/api/category.e2e.test.js index 091219c..f7ceea1 100644 --- a/src/service/api/category.e2e.test.js +++ b/src/service/api/category.e2e.test.js @@ -6,7 +6,131 @@ const {StatusCodes} = require(`http-status-codes`); const category = require(`./category`); const DataService = require(`../data-service/category`); -const mockData = require(`./category.e2e.test.json`); +const mockData = [ + { + id: `pBQYra`, + category: [ + `Софт`, + `Игры`, + `Книги`, + `Любовные романы`, + `Юмор`, + `Хит`, + `Пословицы` + ], + comments: [ + { + id: `Hi-yI9`, + text: `С чем связана продажа? Почему так дешёво? Продаю в связи с переездом. Отрываю от сердца. Почему в таком ужасном состоянии? Оплата наличными или перевод на карту? Неплохо, но дорого.` + }, + { + id: `WUPrFt`, + text: `Оплата наличными или перевод на карту? Почему в таком ужасном состоянии? А где блок питания? С чем связана продажа? Почему так дешёво? Неплохо, но дорого.` + } + ], + description: `Бонусом отдам все аксессуары. Если найдёте дешевле — сброшу цену. Если товар не понравится — верну всё до последней копейки. Даю недельную гарантию.`, + picture: `item03.jpg`, + title: `Куплю антиквариат`, + type: `offer`, + sum: 1986 + }, + { + id: `A0iiyQ`, + category: [ + `Книги`, + `Журналы`, + `Животные`, + `Политика` + ], + comments: [ + { + id: `8cVZgn`, + text: `А где блок питания? А сколько игр в комплекте? Неплохо, но дорого.` + }, + { + id: `nbbDrd`, + text: `Почему в таком ужасном состоянии? Оплата наличными или перевод на карту? Вы что?! В магазине дешевле.` + } + ], + description: `Даю недельную гарантию. Если найдёте дешевле — сброшу цену. При покупке с меня бесплатная доставка в черте города. Если найдётся хозяин товара, валите всё на нас.`, + picture: `item01.jpg`, + title: `Отдам почетные грамоты даром`, + type: `sale`, + sum: 87390 + }, + { + id: `vdv9wu`, + category: [ + `Разное`, + `Посуда` + ], + comments: [ + { + id: `QsPY6h`, + text: `Продаю в связи с переездом. Отрываю от сердца. Совсем немного...` + }, + { + id: `3eqKUq`, + text: `Неплохо, но дорого. Продаю в связи с переездом. Отрываю от сердца.` + } + ], + description: `Товар в отличном состоянии. Второй товар на 50% дешевле. Бонусом отдам все аксессуары. Если товар не понравится — верну всё до последней копейки.`, + picture: `item05.jpg`, + title: `Консультирую по вопросам бихейвиоризма`, + type: `offer`, + sum: 29941 + }, + { + id: `Q4-Z8D`, + category: [ + `Юмор`, + `Разное`, + `Журналы`, + `Хит`, + `Животные`, + `Политика` + ], + comments: [ + { + id: `7RM4Hn`, + text: `Неплохо, но дорого.` + }, + { + id: `YbWxxe`, + text: `Оплата наличными или перевод на карту?` + }, + { + id: `PedWGc`, + text: `Почему в таком ужасном состоянии?` + } + ], + description: `Скидки в честь Дня конституции. Это настоящая находка для коллекционера! Самовывоз в течение дня, вход без масок запрещён. Бонусом отдам все аксессуары.`, + picture: `item06.jpg`, + title: `Продам отличную подборку фильмов на VHS`, + type: `offer`, + sum: 63064 + }, + { + id: `74EUns`, + category: [ + `Юмор`, + `Журналы`, + `Разное`, + `Игры` + ], + comments: [ + { + id: `7_3VQL`, + text: `Почему в таком ужасном состоянии? Вы что?! В магазине дешевле. Продаю в связи с переездом. Отрываю от сердца.` + } + ], + description: `Самовывоз в течение дня, вход без масок запрещён. Бонусом отдам все аксессуары. Пользовались бережно и только по большим праздникам. Второй товар на 50% дешевле.`, + picture: `item10.jpg`, + title: `Куплю мясо по оптовой цене`, + type: `sale`, + sum: 7871 + } +]; const expectedList = [`Софт`, `Игры`, `Книги`, `Любовные романы`, `Юмор`, `Хит`, `Пословицы`, `Журналы`, `Животные`, `Политика`, `Разное`, `Посуда`]; const app = express(); diff --git a/src/service/api/category.e2e.test.json b/src/service/api/category.e2e.test.json deleted file mode 100644 index 5791b1f..0000000 --- a/src/service/api/category.e2e.test.json +++ /dev/null @@ -1,125 +0,0 @@ -[ - { - "id": "pBQYra", - "category": [ - "Софт", - "Игры", - "Книги", - "Любовные романы", - "Юмор", - "Хит", - "Пословицы" - ], - "comments": [ - { - "id": "Hi-yI9", - "text": "С чем связана продажа? Почему так дешёво? Продаю в связи с переездом. Отрываю от сердца. Почему в таком ужасном состоянии? Оплата наличными или перевод на карту? Неплохо, но дорого." - }, - { - "id": "WUPrFt", - "text": "Оплата наличными или перевод на карту? Почему в таком ужасном состоянии? А где блок питания? С чем связана продажа? Почему так дешёво? Неплохо, но дорого." - } - ], - "description": "Бонусом отдам все аксессуары. Если найдёте дешевле — сброшу цену. Если товар не понравится — верну всё до последней копейки. Даю недельную гарантию.", - "picture": "item03.jpg", - "title": "Куплю антиквариат", - "type": "offer", - "sum": 1986 - }, - { - "id": "A0iiyQ", - "category": [ - "Книги", - "Журналы", - "Животные", - "Политика" - ], - "comments": [ - { - "id": "8cVZgn", - "text": "А где блок питания? А сколько игр в комплекте? Неплохо, но дорого." - }, - { - "id": "nbbDrd", - "text": "Почему в таком ужасном состоянии? Оплата наличными или перевод на карту? Вы что?! В магазине дешевле." - } - ], - "description": "Даю недельную гарантию. Если найдёте дешевле — сброшу цену. При покупке с меня бесплатная доставка в черте города. Если найдётся хозяин товара, валите всё на нас.", - "picture": "item01.jpg", - "title": "Отдам почетные грамоты даром", - "type": "sale", - "sum": 87390 - }, - { - "id": "vdv9wu", - "category": [ - "Разное", - "Посуда" - ], - "comments": [ - { - "id": "QsPY6h", - "text": "Продаю в связи с переездом. Отрываю от сердца. Совсем немного..." - }, - { - "id": "3eqKUq", - "text": "Неплохо, но дорого. Продаю в связи с переездом. Отрываю от сердца." - } - ], - "description": "Товар в отличном состоянии. Второй товар на 50% дешевле. Бонусом отдам все аксессуары. Если товар не понравится — верну всё до последней копейки.", - "picture": "item05.jpg", - "title": "Консультирую по вопросам бихейвиоризма", - "type": "offer", - "sum": 29941 - }, - { - "id": "Q4-Z8D", - "category": [ - "Юмор", - "Разное", - "Журналы", - "Хит", - "Животные", - "Политика" - ], - "comments": [ - { - "id": "7RM4Hn", - "text": "Неплохо, но дорого." - }, - { - "id": "YbWxxe", - "text": "Оплата наличными или перевод на карту?" - }, - { - "id": "PedWGc", - "text": "Почему в таком ужасном состоянии?" - } - ], - "description": "Скидки в честь Дня конституции. Это настоящая находка для коллекционера! Самовывоз в течение дня, вход без масок запрещён. Бонусом отдам все аксессуары.", - "picture": "item06.jpg", - "title": "Продам отличную подборку фильмов на VHS", - "type": "offer", - "sum": 63064 - }, - { - "id": "74EUns", - "category": [ - "Юмор", - "Журналы", - "Разное", - "Игры" - ], - "comments": [ - { - "id": "7_3VQL", - "text": "Почему в таком ужасном состоянии? Вы что?! В магазине дешевле. Продаю в связи с переездом. Отрываю от сердца." - } - ], - "description": "Самовывоз в течение дня, вход без масок запрещён. Бонусом отдам все аксессуары. Пользовались бережно и только по большим праздникам. Второй товар на 50% дешевле.", - "picture": "item10.jpg", - "title": "Куплю мясо по оптовой цене", - "type": "sale", - "sum": 7871 - } -] diff --git a/src/service/api/offer.e2e.test.js b/src/service/api/offer.e2e.test.js index befe9b6..81aeb39 100644 --- a/src/service/api/offer.e2e.test.js +++ b/src/service/api/offer.e2e.test.js @@ -7,7 +7,148 @@ const {StatusCodes} = require(`http-status-codes`); const offer = require(`./offer`); const DataService = require(`../data-service/offer`); const CommentsService = require(`../data-service/comment`); -const mockData = require(`./offer.e2e.test.json`); +const mockData = [ + { + id: `DMxd2s`, + category: [ + `Юмор`, + `Разное`, + `Любовные романы`, + `Игры`, + `Книги`, + `Софт` + ], + comments: [ + { + id: `6Y3z9L`, + text: `А где блок питания? Продаю в связи с переездом. Отрываю от сердца. Совсем немного... Почему в таком ужасном состоянии? Неплохо, но дорого. С чем связана продажа? Почему так дешёво?` + }, + { + id: `2WdWw4`, + text: `Продаю в связи с переездом. Отрываю от сердца. А сколько игр в комплекте? Почему в таком ужасном состоянии? А где блок питания? С чем связана продажа? Почему так дешёво? Неплохо, но дорого.` + }, + { + id: `7HtW8b`, + text: `С чем связана продажа? Почему так дешёво? Почему в таком ужасном состоянии? Оплата наличными или перевод на карту? Неплохо, но дорого. А где блок питания? Продаю в связи с переездом. Отрываю от сердца.` + }, + { + id: `idyTWy`, + text: `Продаю в связи с переездом. Отрываю от сердца. Оплата наличными или перевод на карту? Неплохо, но дорого. А где блок питания? С чем связана продажа? Почему так дешёво? Почему в таком ужасном состоянии?` + } + ], + description: `Товар в отличном состоянии. Второй товар на 50% дешевле. Продаю с болью в сердце... Самовывоз в течение дня, вход без масок запрещён.`, + picture: `item15.jpg`, + title: `Научу писать рандомную фигню за 50 рублей`, + type: `offer`, + sum: 28275 + }, + { + id: `-6XwLE`, + category: [ + `Софт`, + `Посуда`, + `Любовные романы`, + `Журналы` + ], + comments: [ + { + id: `lIbiv_`, + text: `Оплата наличными или перевод на карту?` + } + ], + description: `Бонусом отдам все аксессуары. Таких предложений больше нет! Если найдётся хозяин товара, валите всё на нас. Самовывоз в течение дня, вход без масок запрещён.`, + picture: `item06.jpg`, + title: `Продам отличную подборку фильмов на VHS`, + type: `sale`, + sum: 1715 + }, + { + id: `9VgUWl`, + category: [ + `Юмор`, + `Любовные романы`, + `Книги`, + `Игры`, + `Пословицы`, + `Животные`, + `Хит`, + `Политика`, + `Софт`, + `Разное`, + `Журналы` + ], + comments: [ + { + id: `zoV852`, + text: `Оплата наличными или перевод на карту? Продаю в связи с переездом. Отрываю от сердца. Почему в таком ужасном состоянии? А где блок питания? А сколько игр в комплекте? Неплохо, но дорого. С чем связана продажа? Почему так дешёво? Совсем немного...` + }, + { + id: `3vr5kk`, + text: `Совсем немного... Почему в таком ужасном состоянии? Оплата наличными или перевод на карту? Продаю в связи с переездом. Отрываю от сердца. А где блок питания? С чем связана продажа? Почему так дешёво? Неплохо, но дорого. А сколько игр в комплекте?` + }, + { + id: `kH7C1u`, + text: `Совсем немного... Неплохо, но дорого. Почему в таком ужасном состоянии? Продаю в связи с переездом. Отрываю от сердца. Вы что?! В магазине дешевле. А сколько игр в комплекте? С чем связана продажа? Почему так дешёво? Оплата наличными или перевод на карту?` + }, + { + id: `RT73Kr`, + text: `С чем связана продажа? Почему так дешёво? Почему в таком ужасном состоянии? А где блок питания? А сколько игр в комплекте? Совсем немного... Вы что?! В магазине дешевле. Неплохо, но дорого. Продаю в связи с переездом. Отрываю от сердца.` + } + ], + description: `Если найдёте дешевле — сброшу цену. Пользовались бережно и только по большим праздникам. Самовывоз в течение дня, вход без масок запрещён. Второй товар на 50% дешевле.`, + picture: `item15.jpg`, + title: `Продам новую приставку Sony Playstation 5`, + type: `offer`, + sum: 75652 + }, + { + id: `exnrno`, + category: [ + `Политика`, + `Книги`, + `Журналы`, + `Юмор`, + `Разное`, + `Софт`, + `Игры`, + `Животные`, + `Хит`, + `Посуда` + ], + comments: [ + { + id: `2PdaTb`, + text: `Совсем немного... Почему в таком ужасном состоянии? Неплохо, но дорого. А где блок питания? Оплата наличными или перевод на карту? А сколько игр в комплекте?` + }, + { + id: `CQv7PM`, + text: `Оплата наличными или перевод на карту? Вы что?! В магазине дешевле. А сколько игр в комплекте? С чем связана продажа? Почему так дешёво? А где блок питания? Почему в таком ужасном состоянии?` + } + ], + description: `Таких предложений больше нет! Если товар не понравится — верну всё до последней копейки. Пользовались бережно и только по большим праздникам. Если найдёте дешевле — сброшу цену.`, + picture: `item12.jpg`, + title: `Научу писать рандомную фигню за 50 рублей`, + type: `offer`, + sum: 40899 + }, + { + id: `VdPH9y`, + category: [ + `Хит` + ], + comments: [ + { + id: `wu64u0`, + text: `Продаю в связи с переездом. Отрываю от сердца.` + } + ], + description: `Скидки в честь Дня конституции. Самовывоз в течение дня, вход без масок запрещён. Если найдётся хозяин товара, валите всё на нас. Если товар не понравится — верну всё до последней копейки.`, + picture: `item02.jpg`, + title: `Научу писать рандомную фигню за 50 рублей`, + type: `sale`, + sum: 49628 + } +]; const sampleOffer = { category: `Котики`, title: `Дам погладить котика`, diff --git a/src/service/api/offer.e2e.test.json b/src/service/api/offer.e2e.test.json deleted file mode 100644 index b3767c4..0000000 --- a/src/service/api/offer.e2e.test.json +++ /dev/null @@ -1,142 +0,0 @@ -[ - { - "id": "DMxd2s", - "category": [ - "Юмор", - "Разное", - "Любовные романы", - "Игры", - "Книги", - "Софт" - ], - "comments": [ - { - "id": "6Y3z9L", - "text": "А где блок питания? Продаю в связи с переездом. Отрываю от сердца. Совсем немного... Почему в таком ужасном состоянии? Неплохо, но дорого. С чем связана продажа? Почему так дешёво?" - }, - { - "id": "2WdWw4", - "text": "Продаю в связи с переездом. Отрываю от сердца. А сколько игр в комплекте? Почему в таком ужасном состоянии? А где блок питания? С чем связана продажа? Почему так дешёво? Неплохо, но дорого." - }, - { - "id": "7HtW8b", - "text": "С чем связана продажа? Почему так дешёво? Почему в таком ужасном состоянии? Оплата наличными или перевод на карту? Неплохо, но дорого. А где блок питания? Продаю в связи с переездом. Отрываю от сердца." - }, - { - "id": "idyTWy", - "text": "Продаю в связи с переездом. Отрываю от сердца. Оплата наличными или перевод на карту? Неплохо, но дорого. А где блок питания? С чем связана продажа? Почему так дешёво? Почему в таком ужасном состоянии?" - } - ], - "description": "Товар в отличном состоянии. Второй товар на 50% дешевле. Продаю с болью в сердце... Самовывоз в течение дня, вход без масок запрещён.", - "picture": "item15.jpg", - "title": "Научу писать рандомную фигню за 50 рублей", - "type": "offer", - "sum": 28275 - }, - { - "id": "-6XwLE", - "category": [ - "Софт", - "Посуда", - "Любовные романы", - "Журналы" - ], - "comments": [ - { - "id": "lIbiv_", - "text": "Оплата наличными или перевод на карту?" - } - ], - "description": "Бонусом отдам все аксессуары. Таких предложений больше нет! Если найдётся хозяин товара, валите всё на нас. Самовывоз в течение дня, вход без масок запрещён.", - "picture": "item06.jpg", - "title": "Продам отличную подборку фильмов на VHS", - "type": "sale", - "sum": 1715 - }, - { - "id": "9VgUWl", - "category": [ - "Юмор", - "Любовные романы", - "Книги", - "Игры", - "Пословицы", - "Животные", - "Хит", - "Политика", - "Софт", - "Разное", - "Журналы" - ], - "comments": [ - { - "id": "zoV852", - "text": "Оплата наличными или перевод на карту? Продаю в связи с переездом. Отрываю от сердца. Почему в таком ужасном состоянии? А где блок питания? А сколько игр в комплекте? Неплохо, но дорого. С чем связана продажа? Почему так дешёво? Совсем немного..." - }, - { - "id": "3vr5kk", - "text": "Совсем немного... Почему в таком ужасном состоянии? Оплата наличными или перевод на карту? Продаю в связи с переездом. Отрываю от сердца. А где блок питания? С чем связана продажа? Почему так дешёво? Неплохо, но дорого. А сколько игр в комплекте?" - }, - { - "id": "kH7C1u", - "text": "Совсем немного... Неплохо, но дорого. Почему в таком ужасном состоянии? Продаю в связи с переездом. Отрываю от сердца. Вы что?! В магазине дешевле. А сколько игр в комплекте? С чем связана продажа? Почему так дешёво? Оплата наличными или перевод на карту?" - }, - { - "id": "RT73Kr", - "text": "С чем связана продажа? Почему так дешёво? Почему в таком ужасном состоянии? А где блок питания? А сколько игр в комплекте? Совсем немного... Вы что?! В магазине дешевле. Неплохо, но дорого. Продаю в связи с переездом. Отрываю от сердца." - } - ], - "description": "Если найдёте дешевле — сброшу цену. Пользовались бережно и только по большим праздникам. Самовывоз в течение дня, вход без масок запрещён. Второй товар на 50% дешевле.", - "picture": "item15.jpg", - "title": "Продам новую приставку Sony Playstation 5", - "type": "offer", - "sum": 75652 - }, - { - "id": "exnrno", - "category": [ - "Политика", - "Книги", - "Журналы", - "Юмор", - "Разное", - "Софт", - "Игры", - "Животные", - "Хит", - "Посуда" - ], - "comments": [ - { - "id": "2PdaTb", - "text": "Совсем немного... Почему в таком ужасном состоянии? Неплохо, но дорого. А где блок питания? Оплата наличными или перевод на карту? А сколько игр в комплекте?" - }, - { - "id": "CQv7PM", - "text": "Оплата наличными или перевод на карту? Вы что?! В магазине дешевле. А сколько игр в комплекте? С чем связана продажа? Почему так дешёво? А где блок питания? Почему в таком ужасном состоянии?" - } - ], - "description": "Таких предложений больше нет! Если товар не понравится — верну всё до последней копейки. Пользовались бережно и только по большим праздникам. Если найдёте дешевле — сброшу цену.", - "picture": "item12.jpg", - "title": "Научу писать рандомную фигню за 50 рублей", - "type": "offer", - "sum": 40899 - }, - { - "id": "VdPH9y", - "category": [ - "Хит" - ], - "comments": [ - { - "id": "wu64u0", - "text": "Продаю в связи с переездом. Отрываю от сердца." - } - ], - "description": "Скидки в честь Дня конституции. Самовывоз в течение дня, вход без масок запрещён. Если найдётся хозяин товара, валите всё на нас. Если товар не понравится — верну всё до последней копейки.", - "picture": "item02.jpg", - "title": "Научу писать рандомную фигню за 50 рублей", - "type": "sale", - "sum": 49628 - } -] diff --git a/src/service/api/search.e2e.test.js b/src/service/api/search.e2e.test.js index 7f29bc6..728972e 100644 --- a/src/service/api/search.e2e.test.js +++ b/src/service/api/search.e2e.test.js @@ -6,7 +6,154 @@ const {StatusCodes} = require(`http-status-codes`); const search = require(`./search`); const DataService = require(`../data-service/search`); -const mockData = require(`./search.e2e.test.json`); +const mockData = [ + { + id: `Opp9hU`, + category: [ + `Животные`, + `Юмор`, + `Журналы`, + `Пословицы`, + `Разное`, + `Софт`, + `Хит`, + `Игры`, + `Книги`, + `Любовные романы`, + `Посуда` + ], + comments: [ + { + id: `tNIEdn`, + text: `А где блок питания? Неплохо, но дорого.` + } + ], + description: `Если найдётся хозяин товара, валите всё на нас. Самовывоз в течение дня, вход без масок запрещён. При покупке с меня бесплатная доставка в черте города. Пользовались бережно и только по большим праздникам.`, + picture: `item15.jpg`, + title: `Куплю антиквариат`, + type: `sale`, + sum: 50750 + }, + { + id: `HYzW93`, + category: [ + `Юмор`, + `Журналы`, + `Софт`, + `Политика`, + `Игры`, + `Любовные романы`, + `Разное` + ], + comments: [ + { + id: `G_lSyW`, + text: `А где блок питания? Совсем немного... Оплата наличными или перевод на карту? Почему в таком ужасном состоянии? Продаю в связи с переездом. Отрываю от сердца. С чем связана продажа? Почему так дешёво? А сколько игр в комплекте?` + }, + { + id: `3QITPg`, + text: `Почему в таком ужасном состоянии? А где блок питания? А сколько игр в комплекте? Совсем немного... Неплохо, но дорого. Вы что?! В магазине дешевле. Оплата наличными или перевод на карту?` + }, + { + id: `TrEGcq`, + text: `Неплохо, но дорого. Оплата наличными или перевод на карту? А где блок питания? А сколько игр в комплекте? Вы что?! В магазине дешевле. Почему в таком ужасном состоянии? С чем связана продажа? Почему так дешёво?` + } + ], + description: `Самовывоз в течение дня, вход без масок запрещён. При покупке с меня бесплатная доставка в черте города. Скидки в честь Дня конституции. Таких предложений больше нет!`, + picture: `item13.jpg`, + title: `Отдам почетные грамоты даром`, + type: `offer`, + sum: 58746 + }, + { + id: `G6VAhG`, + category: [ + `Разное`, + `Животные`, + `Любовные романы`, + `Посуда`, + `Игры`, + `Софт`, + `Журналы`, + `Пословицы`, + `Книги`, + `Юмор` + ], + comments: [ + { + id: `lyhWDk`, + text: `Оплата наличными или перевод на карту? С чем связана продажа? Почему так дешёво? А где блок питания? Совсем немного... Вы что?! В магазине дешевле. А сколько игр в комплекте?` + } + ], + description: `Если товар не понравится — верну всё до последней копейки. Это настоящая находка для коллекционера! При покупке с меня бесплатная доставка в черте города. Самовывоз в течение дня, вход без масок запрещён.`, + picture: `item02.jpg`, + title: `Научу писать рандомную фигню за 50 рублей`, + type: `sale`, + sum: 35101 + }, + { + id: `Mkh8tu`, + category: [ + `Книги`, + `Животные`, + `Посуда`, + `Хит`, + `Пословицы`, + `Политика`, + `Игры`, + `Юмор`, + `Журналы`, + `Софт`, + `Разное` + ], + comments: [ + { + id: `ypEZLb`, + text: `Оплата наличными или перевод на карту? С чем связана продажа? Почему так дешёво? Почему в таком ужасном состоянии? А сколько игр в комплекте? Неплохо, но дорого. А где блок питания? Вы что?! В магазине дешевле.` + }, + { + id: `gRneP1`, + text: `С чем связана продажа? Почему так дешёво? Неплохо, но дорого. Совсем немного... Вы что?! В магазине дешевле. Почему в таком ужасном состоянии? А сколько игр в комплекте? Продаю в связи с переездом. Отрываю от сердца.` + } + ], + description: `Продаю с болью в сердце... Таких предложений больше нет! Это настоящая находка для коллекционера! Если товар не понравится — верну всё до последней копейки.`, + picture: `item11.jpg`, + title: `Куплю человеческие волосы`, + type: `sale`, + sum: 17590 + }, + { + id: `kT24_W`, + category: [ + `Политика`, + `Разное`, + `Игры` + ], + comments: [ + { + id: `n-nRZx`, + text: `Оплата наличными или перевод на карту? Неплохо, но дорого. А сколько игр в комплекте?` + }, + { + id: `QsuhbX`, + text: `Вы что?! В магазине дешевле. Почему в таком ужасном состоянии? Продаю в связи с переездом. Отрываю от сердца.` + }, + { + id: `gxX4Fb`, + text: `Вы что?! В магазине дешевле. С чем связана продажа? Почему так дешёво? Почему в таком ужасном состоянии?` + }, + { + id: `7XQo8W`, + text: `С чем связана продажа? Почему так дешёво? А где блок питания? А сколько игр в комплекте?` + } + ], + description: `Пользовались бережно и только по большим праздникам. Если найдётся хозяин товара, валите всё на нас. Самовывоз в течение дня, вход без масок запрещён. Скидки в честь Дня конституции.`, + picture: `item09.jpg`, + title: `Куплю человеческие волосы`, + type: `sale`, + sum: 20843 + } +]; const app = express(); app.use(express.json()); @@ -22,7 +169,7 @@ describe(`API returns offer based on search query`, () => { }); test(`Status code 200`, () => expect(response.statusCode).toBe(StatusCodes.OK)); - test(`2 offer found`, () => expect(response.body.length).toBe(2)); + test(`2 offers found`, () => expect(response.body.length).toBe(2)); test(`Offer has correct id`, () => expect(response.body[0].id).toBe(`Mkh8tu`)); }); diff --git a/src/service/api/search.e2e.test.json b/src/service/api/search.e2e.test.json deleted file mode 100644 index f3e2cbd..0000000 --- a/src/service/api/search.e2e.test.json +++ /dev/null @@ -1,148 +0,0 @@ -[ - { - "id": "Opp9hU", - "category": [ - "Животные", - "Юмор", - "Журналы", - "Пословицы", - "Разное", - "Софт", - "Хит", - "Игры", - "Книги", - "Любовные романы", - "Посуда" - ], - "comments": [ - { - "id": "tNIEdn", - "text": "А где блок питания? Неплохо, но дорого." - } - ], - "description": "Если найдётся хозяин товара, валите всё на нас. Самовывоз в течение дня, вход без масок запрещён. При покупке с меня бесплатная доставка в черте города. Пользовались бережно и только по большим праздникам.", - "picture": "item15.jpg", - "title": "Куплю антиквариат", - "type": "sale", - "sum": 50750 - }, - { - "id": "HYzW93", - "category": [ - "Юмор", - "Журналы", - "Софт", - "Политика", - "Игры", - "Любовные романы", - "Разное" - ], - "comments": [ - { - "id": "G_lSyW", - "text": "А где блок питания? Совсем немного... Оплата наличными или перевод на карту? Почему в таком ужасном состоянии? Продаю в связи с переездом. Отрываю от сердца. С чем связана продажа? Почему так дешёво? А сколько игр в комплекте?" - }, - { - "id": "3QITPg", - "text": "Почему в таком ужасном состоянии? А где блок питания? А сколько игр в комплекте? Совсем немного... Неплохо, но дорого. Вы что?! В магазине дешевле. Оплата наличными или перевод на карту?" - }, - { - "id": "TrEGcq", - "text": "Неплохо, но дорого. Оплата наличными или перевод на карту? А где блок питания? А сколько игр в комплекте? Вы что?! В магазине дешевле. Почему в таком ужасном состоянии? С чем связана продажа? Почему так дешёво?" - } - ], - "description": "Самовывоз в течение дня, вход без масок запрещён. При покупке с меня бесплатная доставка в черте города. Скидки в честь Дня конституции. Таких предложений больше нет!", - "picture": "item13.jpg", - "title": "Отдам почетные грамоты даром", - "type": "offer", - "sum": 58746 - }, - { - "id": "G6VAhG", - "category": [ - "Разное", - "Животные", - "Любовные романы", - "Посуда", - "Игры", - "Софт", - "Журналы", - "Пословицы", - "Книги", - "Юмор" - ], - "comments": [ - { - "id": "lyhWDk", - "text": "Оплата наличными или перевод на карту? С чем связана продажа? Почему так дешёво? А где блок питания? Совсем немного... Вы что?! В магазине дешевле. А сколько игр в комплекте?" - } - ], - "description": "Если товар не понравится — верну всё до последней копейки. Это настоящая находка для коллекционера! При покупке с меня бесплатная доставка в черте города. Самовывоз в течение дня, вход без масок запрещён.", - "picture": "item02.jpg", - "title": "Научу писать рандомную фигню за 50 рублей", - "type": "sale", - "sum": 35101 - }, - { - "id": "Mkh8tu", - "category": [ - "Книги", - "Животные", - "Посуда", - "Хит", - "Пословицы", - "Политика", - "Игры", - "Юмор", - "Журналы", - "Софт", - "Разное" - ], - "comments": [ - { - "id": "ypEZLb", - "text": "Оплата наличными или перевод на карту? С чем связана продажа? Почему так дешёво? Почему в таком ужасном состоянии? А сколько игр в комплекте? Неплохо, но дорого. А где блок питания? Вы что?! В магазине дешевле." - }, - { - "id": "gRneP1", - "text": "С чем связана продажа? Почему так дешёво? Неплохо, но дорого. Совсем немного... Вы что?! В магазине дешевле. Почему в таком ужасном состоянии? А сколько игр в комплекте? Продаю в связи с переездом. Отрываю от сердца." - } - ], - "description": "Продаю с болью в сердце... Таких предложений больше нет! Это настоящая находка для коллекционера! Если товар не понравится — верну всё до последней копейки.", - "picture": "item11.jpg", - "title": "Куплю человеческие волосы", - "type": "sale", - "sum": 17590 - }, - { - "id": "kT24_W", - "category": [ - "Политика", - "Разное", - "Игры" - ], - "comments": [ - { - "id": "n-nRZx", - "text": "Оплата наличными или перевод на карту? Неплохо, но дорого. А сколько игр в комплекте?" - }, - { - "id": "QsuhbX", - "text": "Вы что?! В магазине дешевле. Почему в таком ужасном состоянии? Продаю в связи с переездом. Отрываю от сердца." - }, - { - "id": "gxX4Fb", - "text": "Вы что?! В магазине дешевле. С чем связана продажа? Почему так дешёво? Почему в таком ужасном состоянии?" - }, - { - "id": "7XQo8W", - "text": "С чем связана продажа? Почему так дешёво? А где блок питания? А сколько игр в комплекте?" - } - ], - "description": "Пользовались бережно и только по большим праздникам. Если найдётся хозяин товара, валите всё на нас. Самовывоз в течение дня, вход без масок запрещён. Скидки в честь Дня конституции.", - "picture": "item09.jpg", - "title": "Куплю человеческие волосы", - "type": "sale", - "sum": 20843 - } -] diff --git a/src/service/api/search.js b/src/service/api/search.js index 0eff0f6..aa7adc5 100644 --- a/src/service/api/search.js +++ b/src/service/api/search.js @@ -3,7 +3,6 @@ const {Router} = require(`express`); const {StatusCodes} = require(`http-status-codes`); - module.exports = (app, service) => { const route = new Router(); app.use(`/search`, route); diff --git a/src/service/cli/server.js b/src/service/cli/server.js index c5b2eaf..0f5dc3f 100644 --- a/src/service/cli/server.js +++ b/src/service/cli/server.js @@ -1,7 +1,7 @@ 'use strict'; const {StatusCodes, ReasonPhrases} = require(`http-status-codes`); -const {DEFAULT_LOCAL_PORT, ExitCode} = require(`../../constants`); +const {DEFAULT_API_PORT, ExitCode} = require(`../../constants`); const express = require(`express`); const routes = require(`../api`); const {getLogger} = require(`../lib/logger`); @@ -30,14 +30,14 @@ app.use((err, _req, _res, _next) => { module.exports = { name: `--server`, run([customPort]) { - const port = Number.parseInt(customPort, 10) || DEFAULT_LOCAL_PORT; + const port = Number.parseInt(customPort, 10) || DEFAULT_API_PORT; try { app.listen(port, (err) => { if (err) { - return logger.error(`Ошибка при создании сервера: ${err.message}`); + return logger.error(`An error occured on creating server: ${err.message}`); } - return logger.info(`Ожидаю соединений на ${port}`); + return logger.info(`Waiting for connections on ${port}`); }); } catch (err) { logger.error(`An error occured: ${err.message}`); diff --git a/src/utils.js b/src/utils.js index 7ca4223..9fb3938 100644 --- a/src/utils.js +++ b/src/utils.js @@ -4,6 +4,8 @@ const {LogMode} = require(`./constants`); const chalk = require(`chalk`); const {readFile} = require(`fs`).promises; +const NUM_SPLITTING_THRESHOLD = 5; + /** * Выводит число с ведущим нулем для цифры * @@ -96,6 +98,20 @@ const getRandomItems = ({ Restrict = {} }) => shuffle(list.slice()).slice(Restrict.MIN || start, Restrict.MAX || end); +/** + * Возвращает строковое представление пяти- и более -значного числа с отделением тысячных разрядов пробелом + * + * @param {*} num + * @return {String} + */ +const splitNumByThousands = (num) => { + const str = num.toString(); + if (str.length < NUM_SPLITTING_THRESHOLD) { + return str; + } + return str.replace(/(\d)(?=(\d\d\d)+(?!\d))/g, `$1\u00A0`); +}; + module.exports = { formatNumWithLead0, getRandomInt, @@ -103,5 +119,6 @@ module.exports = { getRandomItems, outputRes, readContent, - shuffle + shuffle, + splitNumByThousands };