diff --git a/README.md b/README.md index 08dd3c1..35df581 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,30 @@ -## Ceres Imaging -# FrontEnd Engineer Candidate Screening +## Frontend challenge for Ceres Imaging -### About the assignment +This is a simple earthquake seach feature consuming the data from [USGS API](https://earthquake.usgs.gov/fdsnws/event/1/#parameters) -- There's no right or wrong way to complete this challenge - we want to see how you solve problems! -- You can spend as much time as you need to complete this, but we don't want to take up too much of your time. Solving every problem isn't mandatory. -- You can run the project locally or you can use Docker. If you use Docker please add the Dockerfile. -- Code styling is important - you can use Prettier or any other option. -- A basic scaffold application with Vue/Vuetify app is available. If you don't feel comfortable with Vue, you can start from scratch with React or any other toolset. -- You can use any tool/library that makes your work faster/easier. +### Installing dependencies -### How to do it +1. clone the repo +2. run `npm install` to install necessary dependencies +3. run `npm run serve` to build the local environment -1. Fork the repo -1. Do your magic -1. Push to your fork -1. Create a PR to upstream +### Stack -### Tasks +This project was develop using vue mapbox, vuex and vuetify -1. Create a form in the sidebar to submit filters to the source endpoint including: `starttime`, `endtime` and `minmagnitude` -1. Circle radius must be relative to the earthquake magnitude -1. Add a Popup to the map to show info about clicked point including: `place`, `magnitude` and `time` +- This was my first time using some of these technologies, so I wanted to give it a try. I had to have an intensive ramp-up in order to make it work. -![screenshot](https://user-images.githubusercontent.com/360260/120043690-cabf9780-bfe2-11eb-8771-8e5e79f025f7.png) +- I decided to use vuex to manage the states because it was the easiest and cleanest way to manipulate the states and propagate the data between the components. + +- Finally, I was able to change the color of the `circle-radius` depending of the value of the magnitude + +### Functionability + +The user is able to perfom a search by given three endpoints: `starttime`, `endtime` and `minmagnitude`. These endpoints will submit the filter and return all the data that was requested ### Docs - [USGS API](https://earthquake.usgs.gov/fdsnws/event/1/#parameters) - [Mapbox GL JS](https://docs.mapbox.com/mapbox-gl-js/api/) -- [Vue Mapbox](https://vue-mapbox-gl.meta.fr/) +- [Vue Mapbox](https://vue-mapbox-gl.meta.fr/) \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index a58c9de..029d114 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,14 +5,18 @@ "requires": true, "packages": { "": { + "name": "frontend-screening", "version": "0.1.0", "dependencies": { "@mapbox/mapbox-gl-geocoder": "^4.7.1", "@studiometa/vue-mapbox-gl": "^1.4.7", + "axios": "^1.2.2", "core-js": "^3.6.5", "mapbox-gl": "^1.13.1", "vue": "^2.6.11", - "vuetify": "^2.4.0" + "vue-axios": "^3.5.2", + "vuetify": "^2.4.0", + "vuex": "^3.6.2" }, "devDependencies": { "@vue/cli-plugin-babel": "~4.5.0", @@ -3008,6 +3012,29 @@ "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", "dev": true }, + "node_modules/axios": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.2.2.tgz", + "integrity": "sha512-bz/J4gS2S3I7mpN/YZfGFTqhXTYzRho8Ay38w2otuuDR322KzFIWm/4W2K6gIwvWaws5n+mnb7D1lN9uD+QH6Q==", + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axios/node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/babel-eslint": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz", @@ -6913,10 +6940,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz", - "integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==", - "dev": true, + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", "funding": [ { "type": "individual", @@ -11567,6 +11593,11 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", @@ -14535,6 +14566,15 @@ "resolved": "https://registry.npmjs.org/vue/-/vue-2.6.12.tgz", "integrity": "sha512-uhmLFETqPPNyuLLbsKz6ioJ4q7AZHzD8ZVFNATNyICSZouqP2Sz0rotWQC8UNBF6VGSCs5abnKJoStA6JbCbfg==" }, + "node_modules/vue-axios": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/vue-axios/-/vue-axios-3.5.2.tgz", + "integrity": "sha512-GP+dct7UlAWkl1qoP3ppw0z6jcSua5/IrMpjB5O8bh089iIiJ+hdxPYH2NPEpajlYgkW5EVMP95ttXWdas1O0g==", + "peerDependencies": { + "axios": "*", + "vue": "^3.0.0 || ^2.0.0" + } + }, "node_modules/vue-cli-plugin-vuetify": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/vue-cli-plugin-vuetify/-/vue-cli-plugin-vuetify-2.4.0.tgz", @@ -14876,6 +14916,14 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/vuex": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/vuex/-/vuex-3.6.2.tgz", + "integrity": "sha512-ETW44IqCgBpVomy520DT5jf8n0zoCac+sxWnn+hMe/CzaSejb/eVw2YToiXYX+Ex/AuHHia28vWTq4goAexFbw==", + "peerDependencies": { + "vue": "^2.0.0" + } + }, "node_modules/watchpack": { "version": "1.7.5", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz", @@ -18516,6 +18564,28 @@ "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", "dev": true }, + "axios": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.2.2.tgz", + "integrity": "sha512-bz/J4gS2S3I7mpN/YZfGFTqhXTYzRho8Ay38w2otuuDR322KzFIWm/4W2K6gIwvWaws5n+mnb7D1lN9uD+QH6Q==", + "requires": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + }, + "dependencies": { + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + } + } + }, "babel-eslint": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz", @@ -21676,10 +21746,9 @@ } }, "follow-redirects": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz", - "integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==", - "dev": true + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" }, "for-in": { "version": "1.0.2", @@ -25382,6 +25451,11 @@ "ipaddr.js": "1.9.1" } }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", @@ -27817,6 +27891,12 @@ "resolved": "https://registry.npmjs.org/vue/-/vue-2.6.12.tgz", "integrity": "sha512-uhmLFETqPPNyuLLbsKz6ioJ4q7AZHzD8ZVFNATNyICSZouqP2Sz0rotWQC8UNBF6VGSCs5abnKJoStA6JbCbfg==" }, + "vue-axios": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/vue-axios/-/vue-axios-3.5.2.tgz", + "integrity": "sha512-GP+dct7UlAWkl1qoP3ppw0z6jcSua5/IrMpjB5O8bh089iIiJ+hdxPYH2NPEpajlYgkW5EVMP95ttXWdas1O0g==", + "requires": {} + }, "vue-cli-plugin-vuetify": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/vue-cli-plugin-vuetify/-/vue-cli-plugin-vuetify-2.4.0.tgz", @@ -28073,6 +28153,12 @@ } } }, + "vuex": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/vuex/-/vuex-3.6.2.tgz", + "integrity": "sha512-ETW44IqCgBpVomy520DT5jf8n0zoCac+sxWnn+hMe/CzaSejb/eVw2YToiXYX+Ex/AuHHia28vWTq4goAexFbw==", + "requires": {} + }, "watchpack": { "version": "1.7.5", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz", diff --git a/package.json b/package.json index 945484c..e2c4eda 100644 --- a/package.json +++ b/package.json @@ -10,10 +10,13 @@ "dependencies": { "@mapbox/mapbox-gl-geocoder": "^4.7.1", "@studiometa/vue-mapbox-gl": "^1.4.7", + "axios": "^1.2.2", "core-js": "^3.6.5", "mapbox-gl": "^1.13.1", "vue": "^2.6.11", - "vuetify": "^2.4.0" + "vue-axios": "^3.5.2", + "vuetify": "^2.4.0", + "vuex": "^3.6.2" }, "devDependencies": { "@vue/cli-plugin-babel": "~4.5.0", @@ -44,7 +47,13 @@ "parserOptions": { "parser": "babel-eslint" }, - "rules": {} + "rules": { + "prettier/prettier": + [ "error", + { + "endOfLine": "auto"} + ] + } }, "browserslist": [ "> 1%", diff --git a/src/App.vue b/src/App.vue index 069f0d1..fff8bf2 100644 --- a/src/App.vue +++ b/src/App.vue @@ -4,6 +4,7 @@ + @@ -12,6 +13,7 @@ diff --git a/src/components/ColorInfo.vue b/src/components/ColorInfo.vue new file mode 100644 index 0000000..7d8d495 --- /dev/null +++ b/src/components/ColorInfo.vue @@ -0,0 +1,77 @@ + + + + + diff --git a/src/components/Form.vue b/src/components/Form.vue new file mode 100644 index 0000000..b69a75d --- /dev/null +++ b/src/components/Form.vue @@ -0,0 +1,74 @@ + + + + + diff --git a/src/components/Map.vue b/src/components/Map.vue index 4030388..e7153f6 100644 --- a/src/components/Map.vue +++ b/src/components/Map.vue @@ -1,20 +1,46 @@ + diff --git a/src/components/SideBar.vue b/src/components/SideBar.vue index 11b1143..9d660b9 100644 --- a/src/components/SideBar.vue +++ b/src/components/SideBar.vue @@ -3,12 +3,16 @@ Earthquakes Map -
The form goes here...
+
diff --git a/src/main.js b/src/main.js index 1f83c31..594f2e9 100644 --- a/src/main.js +++ b/src/main.js @@ -3,12 +3,14 @@ import App from "./App.vue"; import vuetify from "./plugins/vuetify"; import VueMapbox from "@studiometa/vue-mapbox-gl"; import "mapbox-gl/dist/mapbox-gl.css"; +import store from "@/store/store"; Vue.use(VueMapbox); Vue.config.productionTip = false; new Vue({ + store, vuetify, render: (h) => h(App), }).$mount("#app"); diff --git a/src/plugins/vuetify.js b/src/plugins/vuetify.js index 4c59904..ca36ed0 100644 --- a/src/plugins/vuetify.js +++ b/src/plugins/vuetify.js @@ -1,6 +1,4 @@ import Vue from "vue"; import Vuetify from "vuetify/lib/framework"; - Vue.use(Vuetify); - export default new Vuetify({}); diff --git a/src/store/store.js b/src/store/store.js new file mode 100644 index 0000000..a024537 --- /dev/null +++ b/src/store/store.js @@ -0,0 +1,44 @@ +import Vue from "vue"; +import Vuex from "vuex"; +import axios from "axios"; +import VueAxios from "vue-axios"; + +Vue.use(Vuex); +Vue.use(VueAxios, axios); + +export default new Vuex.Store({ + state: { + earthQuakes: [], + noData: false, + }, + getters: { + getEarthQuakes: (state) => state.earthQuakes, + setNoData: (state) => state.noData, + }, + actions: { + async fetchEarthquakes({ commit }, { startTime, endTime, minmag }) { + try { + if (startTime && endTime && minmag) { + const data = await Vue.axios.get( + `https://earthquake.usgs.gov/fdsnws/event/1/query?format=geojson&starttime=${startTime}&endtime=${endTime}&minmagnitude=${minmag}` + ); + commit("SET_EARTHQU", data.data.features); + commit("SET_NO_DATA", false); + if (data.data.features.length === 0) { + commit("SET_NO_DATA", true); + } + } + } catch (error) { + throw new Error(`API ${error}`); + } + }, + }, + mutations: { + SET_EARTHQU(state, payload) { + state.earthQuakes = payload; + }, + SET_NO_DATA(state, payload) { + state.noData = payload; + }, + }, +});