├── README.md ├── robot-home.png └── src ├── assets ├── build-a-bot-logo.png └── robot-home.png ├── build ├── PartSelector.vue ├── RobotBuilder-preview.css ├── RobotBuilder-preview.html ├── RobotBuilder.css ├── RobotBuilder.html └── images │ ├── arm-articulated-claw.png │ ├── arm-dual-claw.png │ ├── arm-grabber.png │ ├── arm-propeller.png │ ├── arm-stubby-claw.png │ ├── base-double-wheel.png │ ├── base-rocket.png │ ├── base-single-wheel.png │ ├── base-spring.png │ ├── base-triple-wheel.png │ ├── head-big-eye.png │ ├── head-friendly.png │ ├── head-shredder.png │ ├── head-single-eye.png │ ├── head-surveillance.png │ ├── torso-flexible-gauged.png │ ├── torso-gauged.png │ └── torso-pouch.png ├── cart └── ShoppingCart.vue ├── data └── parts.js ├── header.css ├── parts ├── BrowseParts.vue ├── RobotArms.vue ├── RobotBases.vue ├── RobotHeads.vue └── RobotTorsos.vue ├── search ├── Search.vue ├── useFilters.js ├── usePagination.js └── useSearch.js └── store └── modules └── users.js /README.md: -------------------------------------------------------------------------------- 1 | # vue-fundamentals 2 | Repo for working with the Pluralsight Vue Fundamentals course available here: https://www.pluralsight.com/courses/vuejs-fundamentals 3 | 4 | -------------------------------------------------------------------------------- /robot-home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmcooper/vuejs-fundamentals/a781865e23b0921603c9bfe8931d1980d6e20aa3/robot-home.png -------------------------------------------------------------------------------- /src/assets/build-a-bot-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmcooper/vuejs-fundamentals/a781865e23b0921603c9bfe8931d1980d6e20aa3/src/assets/build-a-bot-logo.png -------------------------------------------------------------------------------- /src/assets/robot-home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmcooper/vuejs-fundamentals/a781865e23b0921603c9bfe8931d1980d6e20aa3/src/assets/robot-home.png -------------------------------------------------------------------------------- /src/build/PartSelector.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 52 | 53 | 159 | -------------------------------------------------------------------------------- /src/build/RobotBuilder-preview.css: -------------------------------------------------------------------------------- 1 | .preview { 2 | position: absolute; 3 | top: -20px; 4 | right: 0; 5 | width: 210px; 6 | height: 210px; 7 | padding: 5px; 8 | } 9 | .preview-content { 10 | border: 1px solid #999; 11 | } 12 | .preview img { 13 | width: 50px; 14 | height: 50px; 15 | } 16 | .rotate-right { 17 | transform: rotate(90deg); 18 | } 19 | .rotate-left { 20 | transform: rotate(-90deg); 21 | } 22 | -------------------------------------------------------------------------------- /src/build/RobotBuilder-preview.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 |
6 |
7 | 8 | 9 | 10 |
11 |
12 | 13 |
14 |
15 |
16 | -------------------------------------------------------------------------------- /src/build/RobotBuilder.css: -------------------------------------------------------------------------------- 1 | .part { 2 | position: relative; 3 | width:165px; 4 | height:165px; 5 | border: 3px solid #aaa; 6 | } 7 | .part img { 8 | width:165px; 9 | } 10 | .top-row { 11 | display:flex; 12 | justify-content: space-around; 13 | } 14 | .middle-row { 15 | display:flex; 16 | justify-content: center; 17 | } 18 | .bottom-row { 19 | display:flex; 20 | justify-content: space-around; 21 | border-top: none; 22 | } 23 | .head { 24 | border-bottom: none; 25 | } 26 | .left { 27 | border-right: none; 28 | } 29 | .right { 30 | border-left: none; 31 | } 32 | .left img { 33 | transform: rotate(-90deg); 34 | } 35 | .right img { 36 | transform: rotate(90deg); 37 | } 38 | .bottom { 39 | border-top: none; 40 | } 41 | .prev-selector { 42 | position: absolute; 43 | z-index:1; 44 | top: -3px; 45 | left: -28px; 46 | width: 25px; 47 | height: 171px; 48 | } 49 | .next-selector { 50 | position: absolute; 51 | z-index:1; 52 | top: -3px; 53 | right: -28px; 54 | width: 25px; 55 | height: 171px; 56 | } 57 | .center .prev-selector, .center .next-selector { 58 | opacity:0.8; 59 | } 60 | .left .prev-selector { 61 | top: -28px; 62 | left: -3px; 63 | width: 144px; 64 | height: 25px; 65 | } 66 | .left .next-selector { 67 | top: auto; 68 | bottom: -28px; 69 | left: -3px; 70 | width: 144px; 71 | height: 25px; 72 | } 73 | .right .prev-selector { 74 | top: -28px; 75 | left: 24px; 76 | width: 144px; 77 | height: 25px; 78 | } 79 | .right .next-selector { 80 | top: auto; 81 | bottom: -28px; 82 | left: 24px; 83 | width: 144px; 84 | height: 25px; 85 | } 86 | .right .next-selector { 87 | right: -3px; 88 | } 89 | -------------------------------------------------------------------------------- /src/build/RobotBuilder.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 | 6 | 7 |
8 |
9 |
10 |
11 | 12 | 13 | 14 |
15 |
16 | 17 | 18 | 19 |
20 |
21 | 22 | 23 | 24 |
25 |
26 |
27 |
28 | 29 | 30 | 31 |
32 |
33 |
34 | -------------------------------------------------------------------------------- /src/build/images/arm-articulated-claw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmcooper/vuejs-fundamentals/a781865e23b0921603c9bfe8931d1980d6e20aa3/src/build/images/arm-articulated-claw.png -------------------------------------------------------------------------------- /src/build/images/arm-dual-claw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmcooper/vuejs-fundamentals/a781865e23b0921603c9bfe8931d1980d6e20aa3/src/build/images/arm-dual-claw.png -------------------------------------------------------------------------------- /src/build/images/arm-grabber.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmcooper/vuejs-fundamentals/a781865e23b0921603c9bfe8931d1980d6e20aa3/src/build/images/arm-grabber.png -------------------------------------------------------------------------------- /src/build/images/arm-propeller.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmcooper/vuejs-fundamentals/a781865e23b0921603c9bfe8931d1980d6e20aa3/src/build/images/arm-propeller.png -------------------------------------------------------------------------------- /src/build/images/arm-stubby-claw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmcooper/vuejs-fundamentals/a781865e23b0921603c9bfe8931d1980d6e20aa3/src/build/images/arm-stubby-claw.png -------------------------------------------------------------------------------- /src/build/images/base-double-wheel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmcooper/vuejs-fundamentals/a781865e23b0921603c9bfe8931d1980d6e20aa3/src/build/images/base-double-wheel.png -------------------------------------------------------------------------------- /src/build/images/base-rocket.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmcooper/vuejs-fundamentals/a781865e23b0921603c9bfe8931d1980d6e20aa3/src/build/images/base-rocket.png -------------------------------------------------------------------------------- /src/build/images/base-single-wheel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmcooper/vuejs-fundamentals/a781865e23b0921603c9bfe8931d1980d6e20aa3/src/build/images/base-single-wheel.png -------------------------------------------------------------------------------- /src/build/images/base-spring.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmcooper/vuejs-fundamentals/a781865e23b0921603c9bfe8931d1980d6e20aa3/src/build/images/base-spring.png -------------------------------------------------------------------------------- /src/build/images/base-triple-wheel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmcooper/vuejs-fundamentals/a781865e23b0921603c9bfe8931d1980d6e20aa3/src/build/images/base-triple-wheel.png -------------------------------------------------------------------------------- /src/build/images/head-big-eye.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmcooper/vuejs-fundamentals/a781865e23b0921603c9bfe8931d1980d6e20aa3/src/build/images/head-big-eye.png -------------------------------------------------------------------------------- /src/build/images/head-friendly.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmcooper/vuejs-fundamentals/a781865e23b0921603c9bfe8931d1980d6e20aa3/src/build/images/head-friendly.png -------------------------------------------------------------------------------- /src/build/images/head-shredder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmcooper/vuejs-fundamentals/a781865e23b0921603c9bfe8931d1980d6e20aa3/src/build/images/head-shredder.png -------------------------------------------------------------------------------- /src/build/images/head-single-eye.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmcooper/vuejs-fundamentals/a781865e23b0921603c9bfe8931d1980d6e20aa3/src/build/images/head-single-eye.png -------------------------------------------------------------------------------- /src/build/images/head-surveillance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmcooper/vuejs-fundamentals/a781865e23b0921603c9bfe8931d1980d6e20aa3/src/build/images/head-surveillance.png -------------------------------------------------------------------------------- /src/build/images/torso-flexible-gauged.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmcooper/vuejs-fundamentals/a781865e23b0921603c9bfe8931d1980d6e20aa3/src/build/images/torso-flexible-gauged.png -------------------------------------------------------------------------------- /src/build/images/torso-gauged.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmcooper/vuejs-fundamentals/a781865e23b0921603c9bfe8931d1980d6e20aa3/src/build/images/torso-gauged.png -------------------------------------------------------------------------------- /src/build/images/torso-pouch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmcooper/vuejs-fundamentals/a781865e23b0921603c9bfe8931d1980d6e20aa3/src/build/images/torso-pouch.png -------------------------------------------------------------------------------- /src/cart/ShoppingCart.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 34 | 35 | 47 | -------------------------------------------------------------------------------- /src/data/parts.js: -------------------------------------------------------------------------------- 1 | const images = require.context('./images', true, /\.png$/) 2 | 3 | const parts = { 4 | heads: [ 5 | { 6 | id: 1, 7 | description: 8 | 'A robot head with an unusually large eye and teloscpic neck -- excellent for exploring high spaces.', 9 | title: 'Large Cyclops', 10 | src: images('./head-big-eye.png'), 11 | type: 'heads', 12 | cost: 1225.5 13 | }, 14 | { 15 | id: 2, 16 | description: 'A friendly robot head with two eyes and a smile -- great for domestic use.', 17 | title: 'Friendly Bot', 18 | src: images('./head-friendly.png'), 19 | cost: 945.0, 20 | type: 'heads', 21 | onSale: true 22 | }, 23 | { 24 | id: 3, 25 | description: 26 | 'A large three-eyed head with a shredder for a mouth -- great for crushing light medals or shredding documents.', 27 | title: 'Shredder', 28 | src: images('./head-shredder.png'), 29 | type: 'heads', 30 | cost: 1275.5 31 | }, 32 | { 33 | id: 4, 34 | description: 35 | 'A simple single-eyed head -- simple and inexpensive.', 36 | title: 'Small Cyclops', 37 | src: images('./head-single-eye.png'), 38 | type: 'heads', 39 | cost: 750.0 40 | }, 41 | { 42 | id: 5, 43 | description: 44 | 'A robot head with three oscillating eyes -- excellent for surveillance.', 45 | title: 'Surveillance Bot', 46 | src: images('./head-surveillance.png'), 47 | type: 'heads', 48 | cost: 1255.5 49 | } 50 | ], 51 | arms: [ 52 | { 53 | id: 1, 54 | description: 'An articulated arm with a claw -- great for reaching around corners or working in tight spaces.', 55 | title: 'Articulated', 56 | src: images('./arm-articulated-claw.png'), 57 | type: 'arms', 58 | cost: 275 59 | }, 60 | { 61 | id: 2, 62 | description: 'An arm with two independent claws -- great when you need an extra hand. Need four hands? Equip your bot with two of these arms.', 63 | title: 'Two Clawed', 64 | src: images('./arm-dual-claw.png'), 65 | type: 'arms', 66 | cost: 285 67 | }, 68 | { 69 | id: 3, 70 | description: 'A telescoping arm with a grabber.', 71 | title: 'Grabber', 72 | src: images('./arm-grabber.png'), 73 | type: 'arms', 74 | cost: 205.5 75 | }, 76 | { 77 | id: 4, 78 | description: 'An arm with a propeller -- good for propulsion or as a cooling fan.', 79 | title: 'Propeller', 80 | src: images('./arm-propeller.png'), 81 | type: 'arms', 82 | cost: 230, 83 | onSale: true 84 | }, 85 | { 86 | id: 5, 87 | description: 'A short and stubby arm with a claw -- simple, but cheap.', 88 | title: 'Stubby Claw', 89 | src: images('./arm-stubby-claw.png'), 90 | type: 'arms', 91 | cost: 125 92 | } 93 | ], 94 | torsos: [ 95 | { 96 | id: 1, 97 | description: 'A torso that can bend slightly at the waist and equiped with a heat guage.', 98 | title: 'Flexible Gauged', 99 | src: images('./torso-flexible-gauged.png'), 100 | type: 'torsos', 101 | cost: 1575 102 | }, 103 | { 104 | id: 2, 105 | description: 'A less flexible torso with a battery gauge.', 106 | title: 'Gauged', 107 | src: images('./torso-gauged.png'), 108 | type: 'torsos', 109 | cost: 1385 110 | }, 111 | { 112 | id: 3, 113 | description: 'A simple torso with a pouch for carrying items.', 114 | title: 'Gauged', 115 | src: images('./torso-pouch.png'), 116 | type: 'torsos', 117 | cost: 785, 118 | onSale: true 119 | } 120 | ], 121 | bases: [ 122 | { 123 | id: 1, 124 | description: 'A two wheeled base with an accelerometer for stability.', 125 | title: 'Double Wheeled', 126 | src: images('./base-double-wheel.png'), 127 | type: 'bases', 128 | cost: 895 129 | }, 130 | { 131 | id: 2, 132 | description: 'A rocket base capable of high speed, controlled flight.', 133 | title: 'Rocket', 134 | src: images('./base-rocket.png'), 135 | type: 'bases', 136 | cost: 1520.5 137 | }, 138 | { 139 | id: 3, 140 | description: 'A single-wheeled base with an accelerometer capable of higher speeds and navigating rougher terrain than the two-wheeled variety.', 141 | title: 'Single Wheeled', 142 | src: images('./base-single-wheel.png'), 143 | type: 'bases', 144 | cost: 1190.5 145 | }, 146 | { 147 | id: 4, 148 | description: 'A spring base - great for reaching high places.', 149 | title: 'Spring', 150 | src: images('./base-spring.png'), 151 | type: 'bases', 152 | cost: 1190.5 153 | }, 154 | { 155 | id: 5, 156 | description: 'An inexpensive three-wheeled base. only capable of slow speeds and can only function on smooth surfaces.', 157 | title: 'Triple Wheeled', 158 | src: images('./base-triple-wheel.png'), 159 | type: 'bases', 160 | cost: 700.5 161 | } 162 | ] 163 | } 164 | export default parts 165 | -------------------------------------------------------------------------------- /src/header.css: -------------------------------------------------------------------------------- 1 | header { 2 | background-color: #999; 3 | width: 1084px; 4 | margin: 0 auto; 5 | } 6 | ul { 7 | padding: 3px; 8 | display: flex; 9 | } 10 | .nav-item { 11 | display: inline-block; 12 | padding: 5px 10px; 13 | font-size: 22px; 14 | border-right: 1px solid #bbb; 15 | } 16 | .logo { 17 | vertical-align: middle; 18 | height: 30px; 19 | } 20 | -------------------------------------------------------------------------------- /src/parts/BrowseParts.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 18 | 19 | 36 | -------------------------------------------------------------------------------- /src/parts/RobotArms.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 23 | -------------------------------------------------------------------------------- /src/parts/RobotBases.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 24 | -------------------------------------------------------------------------------- /src/parts/RobotHeads.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 23 | -------------------------------------------------------------------------------- /src/parts/RobotTorsos.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 24 | -------------------------------------------------------------------------------- /src/search/Search.vue: -------------------------------------------------------------------------------- 1 | 36 | 37 | 81 | 123 | -------------------------------------------------------------------------------- /src/search/useFilters.js: -------------------------------------------------------------------------------- 1 | import { ref, computed, onMounted } from 'vue'; 2 | 3 | function filterResults(results, filters) { 4 | return results.value.filter((part) => filters.value.every( 5 | (filter) => { 6 | const filterField = Object.keys(filter)[0]; 7 | const filterValue = filter[filterField]; 8 | return part[filterField] === filterValue; 9 | }, 10 | )); 11 | } 12 | 13 | export default function useFilters(searchResults) { 14 | const filters = ref([]); 15 | 16 | const applyFilters = (filter) => filters.value.push(filter); 17 | const clearFilters = () => { filters.value = []; }; 18 | 19 | onMounted(() => console.log('Mounted: useFilters')); 20 | 21 | const filteredResults = computed(() => filterResults(searchResults, filters)); 22 | 23 | return { 24 | filters, 25 | applyFilters, 26 | clearFilters, 27 | filteredResults, 28 | }; 29 | } 30 | -------------------------------------------------------------------------------- /src/search/usePagination.js: -------------------------------------------------------------------------------- 1 | import { ref, computed, onMounted } from 'vue'; 2 | 3 | export default function usePagination(filteredSearchResults) { 4 | const pageSize = 5; 5 | const currentPage = ref(1); 6 | const nextPage = () => { currentPage.value += 1; }; 7 | const prevPage = () => { currentPage.value -= 1; }; 8 | 9 | onMounted(() => console.log('Mounted: useSearch')); 10 | 11 | const currentStartIndex = computed( 12 | () => (currentPage.value - 1) * pageSize + 1, 13 | ); 14 | 15 | const currentEndIndex = computed(() => { 16 | const end = currentStartIndex.value - 1 + pageSize; 17 | return end > filteredSearchResults.value.length 18 | ? filteredSearchResults.value.length 19 | : end; 20 | }); 21 | 22 | const pagedResults = computed(() => { 23 | const startIndex = currentStartIndex.value - 1; 24 | const endIndex = currentEndIndex.value; 25 | return filteredSearchResults.value.slice(startIndex, endIndex); 26 | }); 27 | 28 | return { 29 | currentPage, 30 | nextPage, 31 | prevPage, 32 | pageSize, 33 | currentStartIndex, 34 | currentEndIndex, 35 | pagedResults, 36 | }; 37 | } 38 | -------------------------------------------------------------------------------- /src/search/useSearch.js: -------------------------------------------------------------------------------- 1 | import { ref, onMounted } from 'vue'; 2 | import parts from '../data/parts'; 3 | 4 | const allParts = [...parts.heads, ...parts.arms, ...parts.torsos, ...parts.bases]; 5 | 6 | export default function useSearch(originalSearchTerm) { 7 | const results = ref([]); 8 | 9 | const searchInventory = (searchTerm) => { 10 | let searchResults; 11 | const term = searchTerm || originalSearchTerm; 12 | 13 | if (!term) searchResults = allParts; 14 | else { 15 | const lowerTerm = term.toLowerCase(); 16 | searchResults = allParts.filter( 17 | (part) => part.title.toLowerCase().includes(lowerTerm), 18 | ); 19 | } 20 | results.value = [...searchResults]; 21 | }; 22 | 23 | searchInventory(originalSearchTerm); 24 | 25 | onMounted(() => console.log('Mounted: useSearch')); 26 | 27 | return { searchResults: results, search: searchInventory }; 28 | } 29 | -------------------------------------------------------------------------------- /src/store/modules/users.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | export default { 4 | state: { 5 | user: null, 6 | }, 7 | mutations: { 8 | updateCurrentUser(state, user) { 9 | state.user = user; 10 | }, 11 | }, 12 | getters: { 13 | }, 14 | actions: { 15 | signIn({ commit }) { 16 | axios.post('/api/sign-in') 17 | .then(result => commit('updateCurrentUser', result.data)) 18 | .catch(console.error); 19 | }, 20 | }, 21 | }; 22 | --------------------------------------------------------------------------------