Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Camron's Submittion (Part 2 With Vue) #5

Open
wants to merge 32 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
9f4d6d5
Refactor in favor of async/await vs chaining .then()
Camron2889 Jan 2, 2025
be4d5bb
Replace single- with double-quotes
Camron2889 Jan 2, 2025
fe63ac0
Add fetchCategories()
Camron2889 Jan 2, 2025
c97dc8e
Add category parameter to fetchProducts()
Camron2889 Jan 2, 2025
3889ab1
Add filters div
Camron2889 Jan 2, 2025
5e83c2c
Add filter button generation
Camron2889 Jan 2, 2025
3c2d036
Use "data-selected" attr. to handle selected state
Camron2889 Jan 2, 2025
b988e5a
Add updating of products on filter click
Camron2889 Jan 2, 2025
cfba696
Add styling for filter buttons
Camron2889 Jan 2, 2025
0017463
Add appStates object
Camron2889 Jan 2, 2025
c633845
Add event handlers to search button
Camron2889 Jan 2, 2025
660f280
Create vue project
Camron2889 Jan 3, 2025
865b143
Reorganize
Camron2889 Jan 3, 2025
8e573f3
Add sass-embedded
Camron2889 Jan 3, 2025
ae847d6
Add main.scss import
Camron2889 Jan 3, 2025
6600c56
Refactor scss
Camron2889 Jan 3, 2025
cbc6b9b
Tidy imports
Camron2889 Jan 3, 2025
dfc7564
Get ProductGrid working
Camron2889 Jan 3, 2025
90a03b3
Add FIlter and Filters component
Camron2889 Jan 4, 2025
c66335f
Create Pinia store for products
Camron2889 Jan 4, 2025
07f72e0
Finish setting up Pinia
Camron2889 Jan 4, 2025
7490824
Add selected state to filter buttons
Camron2889 Jan 4, 2025
d845992
Integrate Pinia store
Camron2889 Jan 4, 2025
08fd379
Fix indent, add missing setters and getters
Camron2889 Jan 4, 2025
fff8c97
Integrate Pinia store
Camron2889 Jan 4, 2025
1303871
Delete globalVars.scss
Camron2889 Jan 4, 2025
b2ef274
Add filter for "all"
Camron2889 Jan 4, 2025
66d1ca7
Add selection logic for "all" filter
Camron2889 Jan 4, 2025
9190d15
Implement search bar
Camron2889 Jan 4, 2025
89d819e
Make "all" filter only display when rest are loaded
Camron2889 Jan 4, 2025
63332ec
Create .gitignore
Camron2889 Jan 4, 2025
2a13908
Merge branch 'main' into vue
Camron2889 Jan 4, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules/
26 changes: 26 additions & 0 deletions css/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -132,4 +132,30 @@ header .search{
border: none;
border-radius: 100px;
cursor: pointer;
}


.filters {
display: flex;
flex-flow: row nowrap;
justify-content: space-between;
gap: 1em;
}

.filters input.filter {
border-radius: 2em;

padding: 0.4em 1em;
background-color: transparent;

line-height: normal;
font-size: 1.3em;
}

.filters input.filter[data-selected], .filters input.filter:hover {
cursor: pointer;

border-color: black;
background-color: black;
color: white;
}
1 change: 1 addition & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<body>
<header>
<h1>Shop</h1>
<div class="filters"></div>
<div class="search">
<input type="text" placeholder="Search">
<button>
Expand Down
117 changes: 106 additions & 11 deletions js/scripts.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,83 @@
* You are welcome to change and update any code within this file as part of your solution
*/


// State object
const appStates = {
products: {
value: [],
filters: {
contains: ""
},
display: function() {
if (this.filters.contains) {
const filteredProducts = this.value.filter(
product => product.title.toLowerCase().includes(this.filters.contains.toLowerCase())
);
displayProducts(filteredProducts)
} else {
displayProducts(this.value);
}
}
}
};

// Fetch products from the API and display them on the page
document.addEventListener('DOMContentLoaded', () => {
fetchProducts();
document.addEventListener("DOMContentLoaded", async () => {
[products, categories] = await Promise.all([fetchProducts(), fetchCategories()]);
displayFilters(categories);
displayProducts(products);

//Setup search button
const searchElement = document.querySelector(".search");
const searchTextElement = searchElement.querySelector("input[type=\"text\"");
const searchButtonElement = searchElement.querySelector("button");
const onSearch = () => {
const searchText = searchTextElement.value;
appStates.products.filters.contains = searchText;
appStates.products.display();
};
searchButtonElement.addEventListener("click", onSearch);
searchTextElement.addEventListener("keyup", e => {
if (e.key === "Enter") {
onSearch();
}
});
});

// Fetch products from the API
function fetchProducts() {
fetch('https://fakestoreapi.com/products')
async function fetchProducts(category) {
let requestUrl = "https://fakestoreapi.com/products";
if (category) {
requestUrl += `/category/${category}`;
}
return fetch(requestUrl)
.then(response => response.json())
.then(data => {
appStates.products.value = data;
return data;
})
.catch(error => console.error("Error fetching products:", error));
}

// Fetch categories from the API, checking sessionStorage first
async function fetchCategories() {
const categories = sessionStorage.getItem("productCategories");
if (categories) return JSON.parse(categories);

return fetch("https://fakestoreapi.com/products/categories")
.then(response => response.json())
.then(data => {
displayProducts(data);
displayCategories(getAllCategories(data));
sessionStorage.setItem("productCategories", JSON.stringify(data));
return data;
})
.catch(error => console.error('Error fetching products:', error));
.catch(error => console.error("Error fetching categories:", error));
}

// Display all products on the page based on the given data
function displayProducts(products) {
const productGrid = document.querySelector('.product-grid');
productGrid.innerHTML = '';
const productGrid = document.querySelector(".product-grid");
productGrid.innerHTML = "";
products.forEach(product => {
const productElement = createProductElement(product);
productGrid.appendChild(productElement);
Expand All @@ -30,8 +87,8 @@ function displayProducts(products) {

// Function to build each product card element
function createProductElement(product) {
const productElement = document.createElement('article');
productElement.classList.add('product');
const productElement = document.createElement("article");
productElement.classList.add("product");
productElement.innerHTML = `
<img src="${product.image}" alt="${product.title}" class="product-image">
<h2 class="title">${product.title}</h2>
Expand All @@ -41,3 +98,41 @@ function createProductElement(product) {
return productElement;
}

// Display all category filter buttons
function displayFilters(categories) {
let categoriesWithAll = ["all", ...categories];
const filtersElement = document.querySelector(".filters");
categoriesWithAll.forEach(category => {
const filterElement = createFilterElement(category);
filterElement.addEventListener("click", () => {
filtersElement.dataset.category = category;
//Visually deselect filter buttons
[...filtersElement.children].forEach(child => {
delete child.dataset.selected;
});

filterElement.dataset.selected = "";

fetchProducts(category === "all" ? null : category)
.then(products => {
appStates.products.display();
});
});

filtersElement.appendChild(filterElement);
});
}

// Function to build each filter button element
function createFilterElement(category) {
const filterElement = document.createElement("input");
filterElement.setAttribute("type", "button");
filterElement.setAttribute("value", category);
filterElement.classList.add("filter");

if (category === "all") {
filterElement.dataset.selected = "";
}

return filterElement;
}
6 changes: 6 additions & 0 deletions take-home/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[*.{js,jsx,mjs,cjs,ts,tsx,mts,cts,vue}]
charset = utf-8
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
30 changes: 30 additions & 0 deletions take-home/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
.DS_Store
dist
dist-ssr
coverage
*.local

/cypress/videos/
/cypress/screenshots/

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

*.tsbuildinfo
7 changes: 7 additions & 0 deletions take-home/.prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

{
"$schema": "https://json.schemastore.org/prettierrc",
"semi": false,
"singleQuote": true,
"printWidth": 100
}
8 changes: 8 additions & 0 deletions take-home/.vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"recommendations": [
"Vue.volar",
"dbaeumer.vscode-eslint",
"EditorConfig.EditorConfig",
"esbenp.prettier-vscode"
]
}
35 changes: 35 additions & 0 deletions take-home/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# take-home

This template should help get you started developing with Vue 3 in Vite.

## Recommended IDE Setup

[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur).

## Customize configuration

See [Vite Configuration Reference](https://vite.dev/config/).

## Project Setup

```sh
npm install
```

### Compile and Hot-Reload for Development

```sh
npm run dev
```

### Compile and Minify for Production

```sh
npm run build
```

### Lint with [ESLint](https://eslint.org/)

```sh
npm run lint
```
19 changes: 19 additions & 0 deletions take-home/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import js from '@eslint/js'
import pluginVue from 'eslint-plugin-vue'
import skipFormatting from '@vue/eslint-config-prettier/skip-formatting'

export default [
{
name: 'app/files-to-lint',
files: ['**/*.{js,mjs,jsx,vue}'],
},

{
name: 'app/files-to-ignore',
ignores: ['**/dist/**', '**/dist-ssr/**', '**/coverage/**'],
},

js.configs.recommended,
...pluginVue.configs['flat/essential'],
skipFormatting,
]
13 changes: 13 additions & 0 deletions take-home/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Take home</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
8 changes: 8 additions & 0 deletions take-home/jsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"compilerOptions": {
"paths": {
"@/*": ["./src/*"]
}
},
"exclude": ["node_modules", "dist"]
}
29 changes: 29 additions & 0 deletions take-home/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"name": "take-home",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"lint": "eslint . --fix",
"format": "prettier --write src/"
},
"dependencies": {
"pinia": "^2.3.0",
"vue": "^3.5.13",
"vue-router": "^4.5.0"
},
"devDependencies": {
"@eslint/js": "^9.14.0",
"@vitejs/plugin-vue": "^5.2.1",
"@vue/eslint-config-prettier": "^10.1.0",
"eslint": "^9.14.0",
"eslint-plugin-vue": "^9.30.0",
"prettier": "^3.3.3",
"sass-embedded": "^1.83.0",
"vite": "^6.0.5",
"vite-plugin-vue-devtools": "^7.6.8"
}
}
Binary file added take-home/public/favicon.ico
Binary file not shown.
25 changes: 25 additions & 0 deletions take-home/src/App.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<script setup>
import { RouterLink, RouterView } from 'vue-router'
import Filters from './components/Filters.vue'
import Search from './components/Search.vue'
import './assets/main.scss'

</script>

<template>
<header>
<h1>Shop</h1>
<Filters></Filters>
<Search></Search>
</header>
<main>
<RouterView></RouterView>
</main>
<footer>
<p>&copy; 2023 My Simple Site</p>
</footer>
</template>

<style scoped>

</style>
Loading