diff options
Diffstat (limited to 'iridescence/src')
33 files changed, 0 insertions, 2377 deletions
diff --git a/iridescence/src/App.vue b/iridescence/src/App.vue deleted file mode 100644 index 5cd436c..0000000 --- a/iridescence/src/App.vue +++ /dev/null @@ -1,102 +0,0 @@ -<template> - <div id="app"> - <div id="main"> - <Navbar></Navbar> - <BusyBar></BusyBar> - <transition - mode="out-in" - enter-active-class="animate__animated animate__fadeIn animate__faster" - leave-active-class="animate__animated animate__fadeOut animate__faster" - > - <router-view /> - </transition> - </div> - <Footer></Footer> - </div> -</template> - -<script> -import BusyBar from "@/components/BusyBar.vue"; -import Navbar from "@/components/Navbar.vue"; -import Footer from "@/components/Footer.vue"; - -export default { - name: "App", - components: { - BusyBar, - Navbar, - Footer - } -}; -</script> - -<style lang="scss"> -@charset "utf-8"; - -/* Fonts */ -@import url("https://fonts.googleapis.com/css2?family=Oxygen&display=swap"); -$family-sans-serif: "Oxygen", sans-serif; - -/* Custom colors */ -$viridian-green: #2b9292ff; -$cultured: #f4f6f6ff; -$gainsboro: #dee5e5ff; -$queen-blue: #41668cff; -$madder-lake: #cc2936ff; -$rich-black-fogra-29: #001011ff; - -/* Color overrides */ -$primary: $viridian-green; -$danger: $madder-lake; -$info: $queen-blue; -$link: $queen-blue; -$black: $rich-black-fogra-29; -$scheme-main: $cultured; -$scheme-main-bis: $gainsboro; - -/* Resizing */ -$modal-content-width: 50em; - -/* Import only what you need from Bulma */ -@import "../node_modules/bulma/sass/utilities/_all.sass"; -@import "../node_modules/bulma/sass/base/generic.sass"; -@import "../node_modules/bulma/sass/base/minireset.sass"; -@import "../node_modules/bulma/sass/elements/box.sass"; -@import "../node_modules/bulma/sass/elements/other.sass"; -@import "../node_modules/bulma/sass/elements/icon.sass"; -@import "../node_modules/bulma/sass/elements/button.sass"; -@import "../node_modules/bulma/sass/elements/container.sass"; -@import "../node_modules/bulma/sass/elements/content.sass"; -@import "../node_modules/bulma/sass/elements/image.sass"; -@import "../node_modules/bulma/sass/elements/progress.sass"; -@import "../node_modules/bulma/sass/elements/title.sass"; -@import "../node_modules/bulma/sass/form/shared.sass"; -@import "../node_modules/bulma/sass/form/shared.sass"; -@import "../node_modules/bulma/sass/form/input-textarea.sass"; -@import "../node_modules/bulma/sass/form/select.sass"; -@import "../node_modules/bulma/sass/form/file.sass"; -@import "../node_modules/bulma/sass/form/tools.sass"; -@import "../node_modules/bulma/sass/components/card.sass"; -@import "../node_modules/bulma/sass/components/dropdown.sass"; -@import "../node_modules/bulma/sass/components/level.sass"; -@import "../node_modules/bulma/sass/components/modal.sass"; -@import "../node_modules/bulma/sass/components/navbar.sass"; -@import "../node_modules/bulma/sass/components/breadcrumb.sass"; -@import "../node_modules/bulma/sass/grid/columns.sass"; -@import "../node_modules/bulma/sass/helpers/color.sass"; -@import "../node_modules/bulma/sass/helpers/flexbox.sass"; -@import "../node_modules/bulma/sass/helpers/typography.sass"; -@import "../node_modules/bulma/sass/helpers/visibility.sass"; -@import "../node_modules/bulma/sass/layout/section.sass"; -@import "../node_modules/bulma/sass/layout/footer.sass"; - -/* Force footer to bottom of page */ -#app { - display: flex; - min-height: 100vh; - flex-direction: column; -} -#main { - flex: 1; -} -</style> diff --git a/iridescence/src/api/dichroism.js b/iridescence/src/api/dichroism.js deleted file mode 100644 index 93989c5..0000000 --- a/iridescence/src/api/dichroism.js +++ /dev/null @@ -1,81 +0,0 @@ -import Product from "../models/product"; -import PhotoSet from "../models/photo_set"; -import ApiError from "./error"; - -export default class Dichroism { - _base_addr = process.env.VUE_APP_API_BASE_ADDR; - - async createPhoto(file) { - const fd = new FormData(); - fd.append(file.name, file); - - const options = { - method: "POST", - body: fd - }; - - try { - const photos = await this._sendRequest("photos", options); - return photos.map(p => new PhotoSet(p)); - } catch (err) { - console.error("Dichroism: " + err.message); - return null; - } - } - - async getProducts() { - try { - const products = await this._sendRequest("products", null); - return products.map(p => new Product(p)); - } catch (err) { - console.error("Dichroism: " + err.message); - return []; - } - } - - async updateProduct(fieldDiff) { - const options = { - method: "PATCH", - headers: { - "Content-Type": "application/json" - }, - body: JSON.stringify(fieldDiff) - }; - - try { - const product = await this._sendRequest("products", options); - return new Product(product); - } catch (err) { - console.error("Dichroism: " + err.message); - return null; - } - } - - async createProduct(newProduct) { - const options = { - method: "POST", - headers: { - "Content-Type": "application/json" - }, - body: JSON.stringify(newProduct) - }; - - try { - const product = await this._sendRequest("products", options); - return new Product(product); - } catch (err) { - console.error("Dichroism: " + err.message); - return null; - } - } - - async _sendRequest(endpoint, options) { - const response = await fetch(this._base_addr + endpoint, options); - - if (response.ok) { - return await response.json(); - } else { - throw new ApiError(await response.text()); - } - } -} diff --git a/iridescence/src/api/error.js b/iridescence/src/api/error.js deleted file mode 100644 index 7c9320d..0000000 --- a/iridescence/src/api/error.js +++ /dev/null @@ -1,6 +0,0 @@ -export default class ApiError extends Error { - constructor(message) { - super(message); - this.name = "ApiError"; - } -} diff --git a/iridescence/src/assets/logo.png b/iridescence/src/assets/logo.png Binary files differdeleted file mode 100644 index 892376b..0000000 --- a/iridescence/src/assets/logo.png +++ /dev/null diff --git a/iridescence/src/assets/logo_sm.png b/iridescence/src/assets/logo_sm.png Binary files differdeleted file mode 100644 index b74ccfd..0000000 --- a/iridescence/src/assets/logo_sm.png +++ /dev/null diff --git a/iridescence/src/components/BusyBar.vue b/iridescence/src/components/BusyBar.vue deleted file mode 100644 index a38cefa..0000000 --- a/iridescence/src/components/BusyBar.vue +++ /dev/null @@ -1,20 +0,0 @@ -<template> - <div id="busyBar"> - <progress - class="progress is-small is-primary" - max="100" - v-if="isBusy" - ></progress> - </div> -</template> - -<script> -export default { - name: "BusyBar", - computed: { - isBusy() { - return this.$store.state.busy; - } - } -}; -</script> diff --git a/iridescence/src/components/CartCheckout.vue b/iridescence/src/components/CartCheckout.vue deleted file mode 100644 index 1b327b2..0000000 --- a/iridescence/src/components/CartCheckout.vue +++ /dev/null @@ -1,38 +0,0 @@ -<template> - <div id="cartCheckout"> - <div class="buttons has-addons"> - <router-link - v-if="inCart" - to="/cart" - class="button is-success is-rounded" - > - <span class="iconify-inline" data-icon="mdi-cart"></span> - <span>Checkout</span></router-link - > - <a v-else class="button is-static is-rounded"> - <span class="iconify-inline" data-icon="mdi-cart"></span> - </a> - <button class="button is-static">x{{ inCart }}</button> - <button class="button is-static is-rounded"> - {{ cartTotal }} - </button> - </div> - </div> -</template> - -<script> -export default { - name: "CartCheckout", - computed: { - inCart() { - return Object.values(this.$store.state.cart).reduce( - (acc, cur) => acc + cur, - 0 - ); - }, - cartTotal() { - return this.$store.getters.cartTotal; - } - } -}; -</script> diff --git a/iridescence/src/components/Footer.vue b/iridescence/src/components/Footer.vue deleted file mode 100644 index 8210c62..0000000 --- a/iridescence/src/components/Footer.vue +++ /dev/null @@ -1,53 +0,0 @@ -<template> - <div id="footer"> - <footer class="footer"> - <nav class="level"> - <div class="level-item has-text-centered"> - <ul> - <li> - <a - class="navbar-item" - target="_blank" - href="https://www.facebook.com/glassyladiesart" - > - <span class="iconify-inline" data-icon="mdi-facebook"></span> - <span>Like Us on Facebook</span> - </a> - </li> - <li> - <a - class="navbar-item" - target="_blank" - href="mailto:liz@theglassyladies.com" - > - <span class="iconify-inline" data-icon="mdi-email"></span> - <span>Write to Us</span> - </a> - </li> - </ul> - </div> - - <div class="level-item has-text-centered"> - <ul> - <li>© {{ year }} The Glassy Ladies, LLC</li> - <li> - Experiencing issues? - <a href="mailto:webmaster@theglassyladies.com">Let us know.</a> - </li> - </ul> - </div> - </nav> - </footer> - </div> -</template> - -<script> -export default { - name: "Footer", - data() { - return { - year: new Date().getFullYear() - }; - } -}; -</script> diff --git a/iridescence/src/components/Navbar.vue b/iridescence/src/components/Navbar.vue deleted file mode 100644 index 3a3390c..0000000 --- a/iridescence/src/components/Navbar.vue +++ /dev/null @@ -1,114 +0,0 @@ -<template> - <div id="navbar"> - <nav class="navbar is-fixed-top is-primary"> - <div class="navbar-brand"> - <router-link to="/" class="navbar-item"> - <img src="@/assets/logo_sm.png" /> - <div class="navbar-item"> - <span style="color: white"> - The Glassy Ladies - </span> - </div> - </router-link> - <a class="navbar-burger burger" v-on:click="toggleNavMenu"> - <span></span> - <span></span> - <span></span> - </a> - </div> - <div :class="navMenu"> - <div class="navbar-start"> - <router-link to="/faq" class="navbar-item"> - F.A.Q. - </router-link> - <router-link to="/about" class="navbar-item"> - About Us - </router-link> - <router-link to="/care" class="navbar-item"> - Care & Handling - </router-link> - <router-link to="/privacy" class="navbar-item"> - Privacy Policy - </router-link> - <router-link to="/admin" class="navbar-item"> - Inventory - </router-link> - </div> - - <transition - mode="out-in" - enter-active-class="animate__animated animate__flipInX" - leave-active-class="animate__animated animate__flipOutX" - > - <div - class="navbar-end" - v-if="routeName != 'Administration'" - key="cartCheckout" - > - <div - class="navbar-item has-dropdown is-active" - v-for="category in categories" - :key="category" - > - <div class="navbar-link"> - {{ category }} - </div> - </div> - - <div class="navbar-item"> - <CartCheckout></CartCheckout> - </div> - </div> - </transition> - </div> - </nav> - </div> -</template> - -<script> -import CartCheckout from "@/components/CartCheckout.vue"; - -export default { - name: "Navbar", - components: { - CartCheckout - }, - data() { - return { - isMenuActive: false - }; - }, - computed: { - navMenu() { - if (this.isMenuActive) { - return "navbar-menu is-active"; - } else { - return "navbar-menu"; - } - }, - categories() { - //const raw = this.$store.getters.products.map(p => p.category.split("/")); - //const raw = [ - // [1, 2, 3], - // [1, 2, 4], - // [1, 5], - // [6, 7] - //]; - return []; - }, - routeName() { - return this.$route.name; - } - }, - methods: { - generateCategories() { - let results = []; - - return results; - }, - toggleNavMenu() { - this.isMenuActive = !this.isMenuActive; - } - } -}; -</script> diff --git a/iridescence/src/components/ProductCard.vue b/iridescence/src/components/ProductCard.vue deleted file mode 100644 index 144dd9e..0000000 --- a/iridescence/src/components/ProductCard.vue +++ /dev/null @@ -1,74 +0,0 @@ -<template> - <div id="productCard"> - <div class="card"> - <div class="card-image"> - <figure class="image is-square"> - <a @click="setDetailId"> - <img :src="thumbnail" :alt="name" title="Click to expand." /> - </a> - </figure> - </div> - <div class="card-content"> - <div class="content"> - <p class="title is-4"> - <a @click="setDetailId"> - {{ name }} - </a> - </p> - <p class="subtitle is-4"> - {{ dollars }} - </p> - <p class="subtitle is-6">{{ stock }}</p> - </div> - - <div class="content"> - {{ shortDescription }} - </div> - </div> - </div> - </div> -</template> - -<script> -export default { - name: "ProductCard", - props: { - id: Number, - name: String, - quantity: Number, - cents: Number, - photo_thumbnail: String, - photo_base: String, - photo_fullsize: String, - description: String - }, - computed: { - stock() { - if (this.quantity == 0) { - return "Made to order"; - } else { - return [this.quantity, "in stock"].join(" "); - } - }, - dollars() { - return "$ " + (this.cents / 100).toFixed(2); - }, - thumbnail() { - return process.env.VUE_APP_IMAGE_ROOT + this.photo_thumbnail; - }, - shortDescription() { - let description = this.description.split(" "); - if (description.length < 10) { - return this.description; - } else { - return description.splice(0, 10).join(" ") + "…"; - } - } - }, - methods: { - setDetailId() { - this.$store.commit("productDetailId", this.id); - } - } -}; -</script> diff --git a/iridescence/src/components/ProductDetail.vue b/iridescence/src/components/ProductDetail.vue deleted file mode 100644 index a67e39f..0000000 --- a/iridescence/src/components/ProductDetail.vue +++ /dev/null @@ -1,135 +0,0 @@ -<template> - <div id="productDetail"> - <div v-if="product" class="modal is-active"> - <div @click="clearDetailId" class="modal-background"></div> - <div class="modal-content"> - <div class="card"> - <div class="card-image"> - <figure class="image "> - <a target="_blank" :href="fullsize"> - <img - :src="base" - :alt="product.name" - title="Click for fullsize" - /> - </a> - </figure> - </div> - - <div class="card-content"> - <div class="columns"> - <div class="column"> - <p class="title"> - {{ product.name }} - </p> - <nav class="breadcrumb"> - <ul> - <li v-for="category in categories" :key="category"> - {{ category }} - </li> - </ul> - </nav> - <p> - {{ product.description }} - </p> - </div> - <div class="column is-one-third"> - <p class="subtitle">{{ dollars }}</p> - <p class="subtitle">{{ stock }}</p> - <div v-if="inCart" class="field has-addons"> - <p class="control is-expanded"> - <a - class="button is-static is-fullwidth is-rounded is-medium" - > - {{ inCart }} in cart - </a> - </p> - <div class="control"> - <a - @click="incrementCartQuantity(-1)" - class="button is-info is-medium" - > - <span - class="iconify-inline" - data-icon="mdi-cart-minus" - ></span> - </a> - </div> - <div class="control"> - <a - @click="incrementCartQuantity(1)" - class="button is-info is-rounded is-medium" - > - <span - class="iconify-inline" - data-icon="mdi-cart-plus" - ></span> - </a> - </div> - </div> - <button - v-else - @click="incrementCartQuantity(1)" - class="button is-success is-fullwidth is-rounded is-medium" - > - <span - class="iconify-inline" - data-icon="mdi-cart-arrow-down" - ></span> - </button> - </div> - </div> - </div> - </div> - </div> - <div @click="clearDetailId" class="modal-close is-large"></div> - </div> - </div> -</template> - -<script> -export default { - name: "ProductDetail", - computed: { - inCart() { - return this.$store.state.cart[this.product.id]; - }, - product() { - return this.$store.getters.products.find( - p => p.id == this.$store.state.productDetailId - ); - }, - stock() { - if (this.product.quantity == 0) { - return "Made to order"; - } else { - return [this.product.quantity, "in stock"].join(" "); - } - }, - dollars() { - return "$ " + (this.product.cents / 100).toFixed(2); - }, - base() { - return [process.env.VUE_APP_IMAGE_ROOT, this.product.photo_base].join(""); - }, - fullsize() { - return [process.env.VUE_APP_IMAGE_ROOT, this.product.photo_fullsize].join( - "" - ); - }, - categories() { - let categories = this.product.category.split("/"); - categories.splice(0, 1, "All"); - return categories; - } - }, - methods: { - clearDetailId() { - this.$store.commit("productDetailId", 0); - }, - incrementCartQuantity(by) { - this.$store.commit("cartItem", { id: this.product.id, by }); - } - } -}; -</script> diff --git a/iridescence/src/components/ProductFilter.vue b/iridescence/src/components/ProductFilter.vue deleted file mode 100644 index 6fdd461..0000000 --- a/iridescence/src/components/ProductFilter.vue +++ /dev/null @@ -1,52 +0,0 @@ -<template> - <div id="productFilter"> - <aside class="menu"> - <p class="menu-label"> - Availability - </p> - <ul class="menu-list"> - <li> - <label class="radio"> - <input type="radio" name="availability" /> - In stock - </label> - </li> - <li> - <label class="radio"> - <input type="radio" name="availability" /> - Made to order - </label> - </li> - </ul> - <p class="menu-label"> - Category - </p> - <ul class="menu-list"> - <li> - <label class="radio"> - <input type="radio" name="category" /> - Garden - </label> - </li> - <li> - <label class="radio"> - <input type="radio" name="category" /> - Beach - </label> - </li> - <li> - <label class="radio"> - <input type="radio" name="category" /> - Pets - </label> - </li> - </ul> - </aside> - </div> -</template> - -<script> -export default { - name: "ProductFilter" -}; -</script> diff --git a/iridescence/src/components/ProductList.vue b/iridescence/src/components/ProductList.vue deleted file mode 100644 index bed37b3..0000000 --- a/iridescence/src/components/ProductList.vue +++ /dev/null @@ -1,32 +0,0 @@ -<template> - <div id="productsList"> - <div class="columns is-multiline is-variable is-7-desktop"> - <div - class="column is-one-quarter" - v-for="product in products" - :key="product.id" - > - <ProductCard v-bind="product"></ProductCard> - </div> - </div> - </div> -</template> - -<script> -import ProductCard from "@/components/ProductCard.vue"; - -export default { - name: "ProductsList", - components: { - ProductCard - }, - computed: { - products() { - return this.$store.getters.products; - } - }, - created() { - this.$store.dispatch("refreshProducts"); - } -}; -</script> diff --git a/iridescence/src/components/ProductSearch.vue b/iridescence/src/components/ProductSearch.vue deleted file mode 100644 index f60931c..0000000 --- a/iridescence/src/components/ProductSearch.vue +++ /dev/null @@ -1,88 +0,0 @@ -<template> - <div id="productSearch"> - <div class="field is-grouped is-grouped-multiline"> - <div class="control is-expanded has-icons-left"> - <input - class="input is-medium is-primary is-rounded" - type="search" - placeholder="Find something in particular..." - v-model.trim="term" - @input="updateSearch" - autofocus - /> - <span class="icon is-left"> - <span class="iconify-inline" data-icon="mdi-magnify"></span> - </span> - </div> - <div class="control has-icons-left"> - <div class="select is-medium is-primary is-rounded"> - <select v-model="sortOptionName" @change="updateSort"> - <option v-for="name in sortOptionNames" :key="name" :value="name"> - {{ name }} - </option> - </select> - </div> - <span class="icon is-left"> - <span class="iconify-inline" data-icon="mdi-sort"></span> - </span> - </div> - </div> - <content class="has-text-centered" v-if="noResults"> - <p> - Couldn't find what you're looking for? - <a - href="mailto:liz@theglassyladies.com?subject=Custom Order Request&body=Please describe what you are looking for and we will be in touch." - >We do custom orders too!</a - > - </p> - </content> - </div> -</template> - -<script> -export default { - name: "ProductSearch", - data() { - return { - term: "", - searchTimer: function() {}, - sortOptions: { - "Featured Items": a => (a.featured ? -1 : 1), - "In Stock": (a, b) => a.quantity < b.quantity, - "Made to Order": (a, b) => a.quantity > b.quantity, - "A to Z": (a, b) => a.name > b.name, - "Z to A": (a, b) => a.name < b.name, - "Newest to Oldest": (a, b) => a.id < b.id, - "Oldest to Newest": (a, b) => a.id > b.id, - "Price (Low to High)": (a, b) => a.cents > b.cents, - "Price (High to Low)": (a, b) => a.cents < b.cents - }, - sortOptionName: String - }; - }, - created: function() { - this.sortOptionName = this.sortOptionNames[0]; - }, - computed: { - sortOptionNames() { - return Object.keys(this.sortOptions); - }, - noResults() { - return !this.$store.getters.products.length && !this.$store.getters.busy; - } - }, - methods: { - updateSearch() { - clearTimeout(this.searchTimer); - this.timeout = setTimeout(() => { - this.$store.commit("searchTerm", this.term); - }, 1000); - }, - updateSort() { - this.$store.commit("compare", this.sortOptions[this.sortOptionName]); - } - } -}; -</script> - - diff --git a/iridescence/src/components/admin/NewProduct.vue b/iridescence/src/components/admin/NewProduct.vue deleted file mode 100644 index a1d3304..0000000 --- a/iridescence/src/components/admin/NewProduct.vue +++ /dev/null @@ -1,58 +0,0 @@ -<template> - <div id="addNewProduct"> - <button class="button is-primary is-medium is-rounded" @click="toggleModal"> - + Add New - </button> - <transition - enter-active-class="animate__animated animate__fadeIn animate__faster" - leave-active-class="animate__animated animate__fadeOut animate__faster" - > - <div class="modal is-active" v-if="modalEnabled"> - <div class="modal-background"></div> - <div - class="modal-card animate__animated animate__slideInDown animate__faster" - > - <header class="modal-card-head"> - <p class="modal-card-title">Add a new product</p> - <button class="delete" @click="toggleModal"></button> - </header> - <section class="modal-card-body"> - <ProductEditCard v-bind:oldid="-1"></ProductEditCard> - </section> - <footer class="modal-card-foot"></footer> - </div> - </div> - </transition> - </div> -</template> - -<script> -import ProductEditCard from "@/components/admin/ProductEditCard"; - -export default { - name: "NewProduct", - components: { - ProductEditCard - }, - data: function() { - return { - modalEnabled: false - }; - }, - computed: { - numProducts: function() { - return this.$store.getters.products.length; - } - }, - watch: { - numProducts: function() { - this.modalEnabled = false; - } - }, - methods: { - toggleModal() { - this.modalEnabled = !this.modalEnabled; - } - } -}; -</script> diff --git a/iridescence/src/components/admin/ProductEditCard.vue b/iridescence/src/components/admin/ProductEditCard.vue deleted file mode 100644 index 603cb4f..0000000 --- a/iridescence/src/components/admin/ProductEditCard.vue +++ /dev/null @@ -1,282 +0,0 @@ -<template> - <div id="productEditCard"> - <div class="card"> - <div class="card-header"> - <p class="card-header-title" v-if="old.id"> - {{ old.id }}: {{ old.name }} - </p> - </div> - <div class="card-image"> - <figure v-if="old.id" class="image is-square"> - <img :src="thumbnail" :alt="name" /> - </figure> - </div> - <div class="card-content"> - <div class="field"> - <div class="file has-name is-boxed is-centered is-fullwidth "> - <label class="file-label"> - <input - class="file-input" - type="file" - name="image" - accept=".jpg,.jpeg,.JPG,.JPEG" - @change="changePhotoSet" - /> - <span class="file-cta"> - <span class="file-label has-text-centered"> - <p v-if="old.photo_thumbnail"> - Replace Photo - </p> - <p v-else> - Upload Photo - </p> - </span> - </span> - <span v-if="filename" class="file-name"> - {{ filename }} - </span> - <span v-else class="file-name"> - {{ old.photo_thumbnail }} - </span> - </label> - </div> - </div> - <div class="field"> - <input - class="input" - type="text" - placeholder="(product name)" - v-model="name" - /> - </div> - <div class="field"> - <input - class="input" - type="text" - placeholder="(product category)" - v-model="category" - /> - </div> - <div class="field has-addons"> - <p class="control"> - <a class="button is-static"> - # - </a> - </p> - <p class="control is-expanded"> - <input - class="input" - type="text" - placeholder="inventory (quantity)" - v-model="quantity" - /> - </p> - <div class="control"> - <a - class="button is-info is-outlined" - @click="incrementQuantity(-1)" - > - ▼ - </a> - </div> - <div class="control"> - <a class="button is-info is-outlined" @click="incrementQuantity(1)"> - ▲ - </a> - </div> - </div> - <div class="field has-addons"> - <p class="control"> - <a class="button is-static"> - $ - </a> - </p> - <p class="control is-expanded"> - <input - class="input" - type="text" - placeholder="price" - v-model="price" - /> - </p> - </div> - <div class="field"> - <textarea - class="textarea" - type="text" - placeholder="(product description)" - v-model="description" - > - </textarea> - </div> - <div class="field"> - <label class="checkbox"> - <input type="checkbox" v-model="featured" /> - Featured? - </label> - </div> - </div> - <div class="card-footer" v-if="isValidPost || isValidPatch"> - <div class="card-footer-item"> - <button class="button is-primary is-fullwidth" @click="saveProduct"> - Save - </button> - </div> - </div> - </div> - </div> -</template> - -<script> -export default { - name: "ProductEditCard", - data: function() { - return { - filename: "", - diff: {} - }; - }, - props: { - oldid: { - type: Number, - required: true - } - }, - created() { - this.diff = { - id: (this.old && this.old.id) || null, - name: null, - description: null, - cents: null, - quantity: (this.old && this.old.quantity) || 0, - category_path: null, - featured: - this.old && typeof this.old.featured === "boolean" - ? this.old.featured - : false, - photo_set: null - }; - }, - computed: { - thumbnail: function() { - return process.env.VUE_APP_IMAGE_ROOT + this.old.photo_thumbnail; - }, - old: function() { - return this.$store.getters.products.find(p => p.id == this.oldid) || {}; - }, - id: function() { - return this.diff.id || this.old.id; - }, - name: { - get: function() { - return this.diff.name || this.old.name; - }, - set: function(name) { - this.diff.name = name; - } - }, - description: { - get: function() { - return this.diff.description || this.old.description; - }, - set: function(description) { - this.diff.description = description; - } - }, - category: { - get: function() { - return this.diff.category_path || this.old.category; - }, - set: function(category) { - this.diff.category_path = category; - } - }, - featured: { - get: function() { - return this.diff.featured; - }, - set: function(featured) { - this.diff.featured = featured; - } - }, - price: { - get: function() { - const cents = this.diff.cents || this.old.cents || 0; - return (cents / 100).toFixed(0); - }, - set: function(dollars) { - const cents = dollars * 100; - if (isFinite(cents)) { - this.diff.cents = cents; - } - } - }, - quantity: { - get: function() { - return this.diff.quantity || this.old.quantity || 0; - }, - set: function(quantity) { - if (Number.isFinite(quantity)) { - this.diff.quantity = quantity; - } - } - }, - isValidPost: function() { - return ( - !this.diff.id && - this.diff.name && - this.diff.description && - Number.isFinite(this.diff.cents) && - Number.isFinite(this.diff.quantity) && - this.diff.photo_set && - this.diff.category_path && - typeof this.featured === "boolean" - ); - }, - isValidPatch: function() { - return ( - this.diff.id && - (this.diff.photo_set || - (this.diff.name && this.diff.name != this.old.name) || - (Number.isFinite(this.diff.cents) && - this.diff.cents != this.old.cents) || - (this.diff.category_path && - this.diff.category_path != this.old.category) || - (Number.isFinite(this.diff.quantity) && - this.diff.quantity != this.old.quantity) || - (this.diff.description && - this.diff.description != this.old.description) || - this.diff.featured != this.old.featured) - ); - } - }, - methods: { - incrementQuantity(by) { - if (this.quantity + by >= 0) { - this.diff.quantity += by; - } - }, - saveProduct() { - if (this.id) { - // update existing - this.$store.dispatch("updateProduct", this.diff); - } else { - // new product - this.$store.dispatch("createProduct", this.diff); - } - - this.diff.photo_set = null; - }, - changePhotoSet(event) { - const file = event.target.files[0]; - if (!file) { - return; - } - this.$store.dispatch("createPhotoSet", file).then(r => { - this.filename = file.name; - this.diff.photo_set = r[0].id; - }); - } - } -}; -</script> diff --git a/iridescence/src/components/admin/ProductEditList.vue b/iridescence/src/components/admin/ProductEditList.vue deleted file mode 100644 index 91c0929..0000000 --- a/iridescence/src/components/admin/ProductEditList.vue +++ /dev/null @@ -1,32 +0,0 @@ -<template> - <div id="productEditList"> - <div class="columns is-multiline is-variable is-desktop"> - <div - class="column is-one-third-desktop" - v-for="product in products" - :key="product.id" - > - <ProductEditCard v-bind:oldid="product.id"></ProductEditCard> - </div> - </div> - </div> -</template> - -<script> -import ProductEditCard from "@/components/admin/ProductEditCard.vue"; - -export default { - name: "ProductsList", - components: { - ProductEditCard - }, - computed: { - products() { - return this.$store.getters.products; - } - }, - created() { - this.$store.dispatch("refreshProducts"); - } -}; -</script> diff --git a/iridescence/src/components/cart/CartItem.vue b/iridescence/src/components/cart/CartItem.vue deleted file mode 100644 index 20ddec8..0000000 --- a/iridescence/src/components/cart/CartItem.vue +++ /dev/null @@ -1,89 +0,0 @@ -<template> - <div> - <hr /> - <nav class="level"> - <div class="level-left"> - <div class="level-item"> - <p class="image is-64x64"> - <img :src="thumbnail" :title="product.name" /> - </p> - </div> - <div class="level-item"> - <div class="content"> - <strong> - <p> - {{ product.name }} - </p> - </strong> - </div> - </div> - </div> - <div class="level-right"> - <div class="level-item">Subtotal: {{ dollars }}</div> - <div class="level-item"> - <div class="field has-addons"> - <p class="control is-expanded"> - <a class="button is-static is-fullwidth is-rounded"> - {{ inCart }} in cart - </a> - </p> - <div class="control"> - <a - @click="incrementCartQuantity(-1)" - class="button is-info is-rounded" - > - <span class="iconify-inline" data-icon="mdi-cart-minus"></span> - </a> - </div> - <div class="control"> - <a - @click="incrementCartQuantity(1)" - class="button is-info is-rounded" - > - <span class="iconify-inline" data-icon="mdi-cart-plus"></span> - </a> - </div> - </div> - </div> - - <div class="level-item"> - <button class="button is-danger is-rounded" @click="removeAll"> - <span class="iconify-inline" data-icon="mdi-cart-remove"></span> - <span>Remove</span> - </button> - </div> - </div> - </nav> - </div> -</template> - -<script> -export default { - name: "CartItem", - props: { - id: Number, - inCart: Number - }, - computed: { - product() { - return this.$store.state.products.find(p => p.id == this.id); - }, - dollars() { - return "$ " + ((this.product.cents * this.inCart) / 100).toFixed(2); - }, - thumbnail() { - return ( - process.env.VUE_APP_IMAGE_ROOT + "/" + this.product.photo_thumbnail - ); - } - }, - methods: { - incrementCartQuantity(by) { - this.$store.commit("cartItem", { id: this.id, by }); - }, - removeAll() { - this.$store.commit("removeItemFromCart", this.id); - } - } -}; -</script> diff --git a/iridescence/src/components/cart/Totals.vue b/iridescence/src/components/cart/Totals.vue deleted file mode 100644 index 36c9e77..0000000 --- a/iridescence/src/components/cart/Totals.vue +++ /dev/null @@ -1,28 +0,0 @@ -<template> - <div> - <h1 class="subtitle has-text-centered"> - Total - </h1> - <hr /> - - <h1 class="title has-text-centered">{{ cartTotal }}</h1> - <router-link - to="/checkout" - class="button is-success is-fullwidth is-rounded is-medium" - > - Continue to Checkout - </router-link> - </div> -</template> - -<script> -export default { - name: "Totals", - computed: { - cartTotal() { - return this.$store.getters.cartTotal; - } - }, - methods: {} -}; -</script> diff --git a/iridescence/src/components/checkout/CheckoutForm.vue b/iridescence/src/components/checkout/CheckoutForm.vue deleted file mode 100644 index 482baec..0000000 --- a/iridescence/src/components/checkout/CheckoutForm.vue +++ /dev/null @@ -1,301 +0,0 @@ -<template> - <div class="container"> - <form action="#"> - <section class="section"> - <h1 class="title">Customer Info</h1> - <hr /> - - <div class="columns"> - <div class="column"> - <div class="field"> - <label for="firstName" class="label">First Name</label> - - <div class="control has-icons-left"> - <input - v-model="formData.firstName" - id="firstName" - class="input is-rounded" - type="text" - required - /> - <span class="icon is-left"> - <span - class="iconify-inline" - data-icon="mdi-card-account-details" - ></span> - </span> - </div> - </div> - <div class="field"> - <label class="label">Last Name</label> - - <div class="control has-icons-left"> - <input - v-model="formData.lastName" - class="input is-rounded" - type="text" - required - /> - <span class="icon is-left"> - <span - class="iconify-inline" - data-icon="mdi-card-account-details" - ></span> - </span> - </div> - </div> - - <div class="field"> - <label class="label">Email Address</label> - - <div class="control has-icons-left"> - <input - v-model="formData.email" - class="input is-rounded" - type="email" - required - /> - <span class="icon is-left"> - <span class="iconify-inline" data-icon="mdi-email"></span> - </span> - </div> - <p class="help">Example: you@example.com</p> - </div> - - <div class="field"> - <label class="label">Phone Number</label> - <div class="control has-icons-left"> - <input - v-model="formData.phone" - class="input is-rounded" - type="tel" - pattern="[0-9]{10}" - required - /> - <span class="icon is-left"> - <span - class="iconify-inline" - data-icon="mdi-phone-classic" - ></span> - </span> - </div> - <p class="help">Example: 7571234567</p> - </div> - - <label class="checkbox"> - <input v-model="formData.sms" type="checkbox" /> - <span class="iconify" data-icon="mdi-cellphone-android"></span> - May we text order updates and questions to this number? - </label> - </div> - <div class="column"> - <label class="label">Location</label> - <div class="field has-addons"> - <div class="control has-icons-left"> - <div class="select is-rounded"> - <select required> - <option selected>USA</option> - </select> - </div> - <span class="icon is-left"> - <span - class="iconify-inline" - data-icon="mdi-flag-variant" - ></span> - </span> - </div> - - <div class="control has-icons-left"> - <div class="select is-rounded"> - <select v-model="formData.state" required> - <option v-for="state in states" :key="state">{{ - state - }}</option> - </select> - </div> - <span class="icon is-left"> - <span class="iconify-inline" data-icon="mdi-map"></span> - </span> - </div> - </div> - <p class="help"> - If you are located outside of the United States, please - <a href="mailto:liz@theglassyladies.com" - >contact us directly regarding your order</a - > - </p> - - <div class="field"> - <label class="label">Address</label> - - <div class="control has-icons-left"> - <input - v-model="formData.address1" - class="input is-rounded" - type="text" - required - /> - <span class="icon is-left"> - <span - class="iconify-inline" - data-icon="mdi-road-variant" - ></span> - </span> - </div> - </div> - <div class="field"> - <label class="label">Address 2 (Optional)</label> - - <div class="control has-icons-left"> - <input - v-model="formData.address2" - class="input is-rounded" - type="text" - /> - <span class="icon is-left"> - <span - class="iconify-inline" - data-icon="mdi-format-list-numbered" - ></span> - </span> - </div> - </div> - - <div class="field"> - <label class="label">City</label> - <div class="control has-icons-left"> - <input - v-model="formData.city" - class="input is-rounded" - type="text" - required - /> - <span class="icon is-left"> - <span class="iconify-inline" data-icon="mdi-city"></span> - </span> - </div> - </div> - - <div class="field"> - <label class="label">Zip Code</label> - - <div class="control has-icons-left"> - <input - v-model="formData.zip" - class="input is-rounded" - type="text" - pattern="[0-9]{5}" - required - /> - <span class="icon is-left"> - <span class="iconify-inline" data-icon="mdi-mailbox"></span> - </span> - </div> - </div> - </div> - </div> - </section> - - <section class="section"> - <h1 class="title">Payment & Delivery</h1> - <hr /> - - <div class="content"> - <p> - You will be contacted directly regarding payment and delivery - options. - </p> - </div> - </section> - - <section class="section"> - <h1 class="title">Finish</h1> - <hr /> - <h1 class="subtitle">Confirm your order of {{ cartTotal }}?</h1> - <button class="button is-success is-rounded is-medium"> - Place Order - </button> - </section> - </form> - </div> -</template> - -<script> -export default { - name: "CheckoutForm", - data() { - return { - states: [ - "Virginia", - "Alabama", - "Alaska", - "Arizona", - "Arkansas", - "California", - "Colorado", - "Connecticut", - "Delaware", - "Florida", - "Georgia", - "Hawaii", - "Idaho", - "Illinois", - "Indiana", - "Iowa", - "Kansas", - "Kentucky", - "Louisiana", - "Maine", - "Maryland", - "Massachusetts", - "Michigan", - "Minnesota", - "Mississippi", - "Missouri", - "Montana", - "Nebraska", - "Nevada", - "New Hampshire", - "New Jersey", - "New Mexico", - "New York", - "North Carolina", - "North Dakota", - "Ohio", - "Oklahoma", - "Oregon", - "Pennsylvania", - "Rhode Island", - "South Carolina", - "South Dakota", - "Tennessee", - "Texas", - "Utah", - "Vermont", - "Washington", - "West Virginia", - "Wisconsin", - "Wyoming" - ], - formData: { - firstName: "", - lastName: "", - email: "", - phone: "", - address1: "", - address2: "", - city: "", - state: "Virginia", - country: "USA", - zip: "", - sms: false - } - }; - }, - computed: { - cartTotal() { - return this.$store.getters.cartTotal; - } - } -}; -</script> diff --git a/iridescence/src/main.js b/iridescence/src/main.js deleted file mode 100644 index 62e6485..0000000 --- a/iridescence/src/main.js +++ /dev/null @@ -1,13 +0,0 @@ -import Vue from "vue"; -import App from "./App.vue"; -import router from "./router"; -import store from "./store"; -import "animate.css"; - -Vue.config.productionTip = false; - -new Vue({ - router, - store, - render: h => h(App) -}).$mount("#app"); diff --git a/iridescence/src/models/photo_set.js b/iridescence/src/models/photo_set.js deleted file mode 100644 index 7d7213c..0000000 --- a/iridescence/src/models/photo_set.js +++ /dev/null @@ -1,10 +0,0 @@ -export default class PhotoSet { - id = 0; - fullsize = ""; - thumbnail = ""; - base = ""; - - constructor(json) { - Object.assign(this, json); - } -} diff --git a/iridescence/src/models/product.js b/iridescence/src/models/product.js deleted file mode 100644 index ded5434..0000000 --- a/iridescence/src/models/product.js +++ /dev/null @@ -1,16 +0,0 @@ -export default class Product { - constructor(json) { - if (json) { - this.id = json.id ? json.id : null; - this.name = json.name ? json.name : null; - this.description = json.description ? json.description : null; - this.cents = Number.isFinite(json.cents) ? json.cents : null; - this.quantity = Number.isFinite(json.quantity) ? json.quantity : null; - this.featured = json.featured ? json.featured : false; - this.category = json.category ? json.category : null; - this.photo_base = json.photo_base ? json.photo_base : null; - this.photo_thumbnail = json.photo_thumbnail ? json.photo_thumbnail : null; - this.photo_fullsize = json.photo_fullsize ? json.photo_fullsize : null; - } - } -} diff --git a/iridescence/src/router/index.js b/iridescence/src/router/index.js deleted file mode 100644 index 1dc6e63..0000000 --- a/iridescence/src/router/index.js +++ /dev/null @@ -1,58 +0,0 @@ -import Vue from "vue"; -import VueRouter from "vue-router"; -import Home from "../views/Home.vue"; -import Care from "../views/Care.vue"; -import Faq from "../views/Faq.vue"; -import Admin from "../views/Admin.vue"; -import Cart from "../views/Cart.vue"; -import Checkout from "../views/Checkout.vue"; -import Privacy from "../views/Privacy.vue"; - -Vue.use(VueRouter); - -const routes = [ - { - path: "/", - name: "Home", - component: Home - }, - { - path: "/admin", - name: "Administration", - component: Admin - }, - { - path: "/cart", - name: "Cart", - component: Cart - }, - { - path: "/checkout", - name: "Checkout", - component: Checkout - }, - { - path: "/faq", - name: "F.A.Q.", - component: Faq - }, - { - path: "/care", - name: "Care & Handling", - component: Care - }, - { - path: "/privacy", - name: "Privacy", - component: Privacy - } -]; - -const router = new VueRouter({ - mode: "history", - base: process.env.BASE_URL, - routes, - linkExactActiveClass: "is-active" -}); - -export default router; diff --git a/iridescence/src/store/index.js b/iridescence/src/store/index.js deleted file mode 100644 index 709dc79..0000000 --- a/iridescence/src/store/index.js +++ /dev/null @@ -1,118 +0,0 @@ -import Vue from "vue"; -import Vuex from "vuex"; -import Dichroism from "@/api/dichroism.js"; - -Vue.use(Vuex); - -let dichroism = new Dichroism(); - -export default new Vuex.Store({ - state: { - searchTerm: "", - products: [], - cart: {}, - busy: false, - compare: a => (a.featured ? -1 : 1), - productDetailId: 0 - }, - getters: { - products(state) { - let term = state.searchTerm.toLowerCase(); - return state.products - .filter(item => { - return [item.id, item.name, item.description, item.category] - .join("") - .toLowerCase() - .includes(term); - }) - .sort(state.compare); - }, - cartTotal(state) { - let cents = state.products - .filter(p => state.cart[p.id]) - .map(p => p.cents * state.cart[p.id]) - .reduce((acc, cur) => acc + cur, 0); - return "$ " + (cents / 100).toFixed(2); - } - }, - mutations: { - toggleBusy(state) { - state.busy = !state.busy; - }, - productDetailId(state, id) { - state.productDetailId = id; - }, - cartItem(state, { id, by }) { - let newCount = 1; - if (state.cart[id]) { - newCount = state.cart[id] += by; - //if (newCount <= 0) { - // // remove from cart entirely - // delete state.cart[id]; - // return; - //} - //state.cart = { - // ...state.cart, - // [id]: newCount - } - - let cart = { - ...state.cart - }; - - if (newCount) { - cart[id] = newCount; - } else { - // remove entirely - delete cart[id]; - } - state.cart = cart; - }, - removeItemFromCart(state, id) { - if (state.cart[id]) { - let cart = { - ...state.cart - }; - delete cart[id]; - state.cart = cart; - } - }, - compare(state, compare) { - state.compare = compare; - }, - searchTerm(state, term) { - state.searchTerm = term; - }, - setProducts(state, products) { - if (products) { - state.products = products; - } - } - }, - actions: { - async refreshProducts({ commit }) { - commit("toggleBusy"); - const products = await dichroism.getProducts(); - commit("setProducts", products); - commit("toggleBusy"); - }, - async updateProduct({ commit, dispatch }, product) { - commit("toggleBusy"); - await dichroism.updateProduct(product); - dispatch("refreshProducts"); - commit("toggleBusy"); - }, - async createProduct({ commit, dispatch }, product) { - commit("toggleBusy"); - await dichroism.createProduct(product); - dispatch("refreshProducts"); - commit("toggleBusy"); - }, - async createPhotoSet({ commit }, file) { - commit("toggleBusy"); - const photoSet = await dichroism.createPhoto(file); - commit("toggleBusy"); - return photoSet; - } - } -}); diff --git a/iridescence/src/views/About.vue b/iridescence/src/views/About.vue deleted file mode 100644 index 87c483a..0000000 --- a/iridescence/src/views/About.vue +++ /dev/null @@ -1,9 +0,0 @@ -<template> - <div id="about" class="container"> - <section class="section"> - <article class="content"> - <h1>This is an about page</h1> - </article> - </section> - </div> -</template> diff --git a/iridescence/src/views/Admin.vue b/iridescence/src/views/Admin.vue deleted file mode 100644 index eedafc7..0000000 --- a/iridescence/src/views/Admin.vue +++ /dev/null @@ -1,48 +0,0 @@ -<template> - <div id="admin"> - <div class="container"> - <section class="section"> - <div class="columns"> - <div class="column is-narrow"> - <NewProduct></NewProduct> - </div> - <div class="column"> - <ProductSearch></ProductSearch> - </div> - </div> - <ProductEditList></ProductEditList> - </section> - </div> - </div> -</template> - -<script> -import NewProduct from "@/components/admin/NewProduct.vue"; -import ProductSearch from "@/components/ProductSearch.vue"; -import ProductEditList from "@/components/admin/ProductEditList.vue"; - -export default { - name: "Admin", - components: { - ProductEditList, - NewProduct, - ProductSearch - }, - beforeRouteEnter: (to, from, next) => { - if (!process.env.VUE_APP_LOGIN_URL) { - next(); - } else { - fetch(process.env.VUE_APP_LOGIN_URL).then(res => { - if (res.status == 401) { - next(false); - } else { - next(); - } - }); - } - }, - mounted() { - window.scrollTo(0, 0); - } -}; -</script> diff --git a/iridescence/src/views/Care.vue b/iridescence/src/views/Care.vue deleted file mode 100644 index ba49786..0000000 --- a/iridescence/src/views/Care.vue +++ /dev/null @@ -1,10 +0,0 @@ - -<template> - <div id="care" class="container"> - <section class="section"> - <article class="content"> - <h1>This is care and handling page</h1> - </article> - </section> - </div> -</template> diff --git a/iridescence/src/views/Cart.vue b/iridescence/src/views/Cart.vue deleted file mode 100644 index 26949f5..0000000 --- a/iridescence/src/views/Cart.vue +++ /dev/null @@ -1,48 +0,0 @@ -<template> - <div id="cart"> - <div class="container"> - <div v-if="items.length" class="section"> - <div class="columns"> - <div class="column is-one-third"> - <Totals></Totals> - </div> - <div class="column"> - <h1 class="subtitle">Your Shopping Cart</h1> - <CartItem - v-for="item in items" - :key="item[0]" - v-bind:id="item[0] * 1" - v-bind:in-cart="item[1]" - ></CartItem> - </div> - </div> - </div> - <div v-else class="section"> - <div class="content"> - <p class="has-text-centered"> - There's nothing in your cart. - <a> <router-link to="/">Start shopping!</router-link></a> - </p> - </div> - </div> - </div> - </div> -</template> - -<script> -import CartItem from "@/components/cart/CartItem.vue"; -import Totals from "@/components/cart/Totals.vue"; - -export default { - name: "Cart", - computed: { - items() { - return Object.entries(this.$store.state.cart); - } - }, - components: { - CartItem, - Totals - } -}; -</script> diff --git a/iridescence/src/views/Checkout.vue b/iridescence/src/views/Checkout.vue deleted file mode 100644 index 01519ee..0000000 --- a/iridescence/src/views/Checkout.vue +++ /dev/null @@ -1,13 +0,0 @@ -<template> - <div id="checkout"> - <CheckoutForm></CheckoutForm> - </div> -</template> - -<script> -import CheckoutForm from "@/components/checkout/CheckoutForm.vue"; -export default { - name: "Checkout", - components: { CheckoutForm } -}; -</script> diff --git a/iridescence/src/views/Faq.vue b/iridescence/src/views/Faq.vue deleted file mode 100644 index 0497393..0000000 --- a/iridescence/src/views/Faq.vue +++ /dev/null @@ -1,9 +0,0 @@ -<template> - <div id="Faq" class="container"> - <section class="section"> - <article class="content"> - <h1>This is an F.A.Q. page</h1> - </article> - </section> - </div> -</template> diff --git a/iridescence/src/views/Home.vue b/iridescence/src/views/Home.vue deleted file mode 100644 index b63f16f..0000000 --- a/iridescence/src/views/Home.vue +++ /dev/null @@ -1,39 +0,0 @@ -<template> - <!-- Home is a view for browsing through the primary inventory. It should - allow users to sort, filter, and search for items and add them to their - cart. --> - - <div id="home"> - <ProductDetail></ProductDetail> - <div class="columns"> - <div class="column"> - <div class="container"> - <section class="section"> - <ProductSearch></ProductSearch> - </section> - <section class="section"> - <ProductList></ProductList> - </section> - </div> - </div> - </div> - </div> -</template> - -<script> -import ProductList from "@/components/ProductList.vue"; -import ProductSearch from "@/components/ProductSearch.vue"; -import ProductDetail from "@/components/ProductDetail.vue"; - -export default { - name: "Home", - components: { - ProductList, - ProductDetail, - ProductSearch - }, - mounted() { - window.scrollTo(0, 0); - } -}; -</script> diff --git a/iridescence/src/views/Privacy.vue b/iridescence/src/views/Privacy.vue deleted file mode 100644 index 61b0269..0000000 --- a/iridescence/src/views/Privacy.vue +++ /dev/null @@ -1,401 +0,0 @@ -<template> - <div id="privacy" class="container"> - <section class="section"> - <article class="content"> - <h1>Privacy Policy</h1> - <p>Last updated: December 21, 2020</p> - <p> - This Privacy Policy describes Our policies and procedures on the - collection, use and disclosure of Your information when You use the - Service and tells You about Your privacy rights and how the law - protects You. - </p> - <p> - We use Your Personal data to provide and improve the Service. By using - the Service, You agree to the collection and use of information in - accordance with this Privacy Policy. This Privacy Policy has been - created with the help of the - <a - href="https://www.privacypolicies.com/privacy-policy-generator/" - target="_blank" - >Privacy Policy Generator</a - >. - </p> - <h1>Interpretation and Definitions</h1> - <h2>Interpretation</h2> - <p> - The words of which the initial letter is capitalized have meanings - defined under the following conditions. The following definitions - shall have the same meaning regardless of whether they appear in - singular or in plural. - </p> - <h2>Definitions</h2> - <p>For the purposes of this Privacy Policy:</p> - <ul> - <li> - <p> - <strong>Account</strong> means a unique account created for You to - access our Service or parts of our Service. - </p> - </li> - <li> - <p> - <strong>Company</strong> (referred to as either "the - Company", "We", "Us" or "Our" - in this Agreement) refers to The Glassy Ladies LLC, 4201 Munford - Ln, Chesapeake, VA 23321. - </p> - </li> - <li> - <p> - <strong>Cookies</strong> are small files that are placed on Your - computer, mobile device or any other device by a website, - containing the details of Your browsing history on that website - among its many uses. - </p> - </li> - <li> - <p><strong>Country</strong> refers to: Virginia, United States</p> - </li> - <li> - <p> - <strong>Device</strong> means any device that can access the - Service such as a computer, a cellphone or a digital tablet. - </p> - </li> - <li> - <p> - <strong>Personal Data</strong> is any information that relates to - an identified or identifiable individual. - </p> - </li> - <li> - <p><strong>Service</strong> refers to the Website.</p> - </li> - <li> - <p> - <strong>Service Provider</strong> means any natural or legal - person who processes the data on behalf of the Company. It refers - to third-party companies or individuals employed by the Company to - facilitate the Service, to provide the Service on behalf of the - Company, to perform services related to the Service or to assist - the Company in analyzing how the Service is used. - </p> - </li> - <li> - <p> - <strong>Third-party Social Media Service</strong> refers to any - website or any social network website through which a User can log - in or create an account to use the Service. - </p> - </li> - <li> - <p> - <strong>Usage Data</strong> refers to data collected - automatically, either generated by the use of the Service or from - the Service infrastructure itself (for example, the duration of a - page visit). - </p> - </li> - <li> - <p> - <strong>Website</strong> refers to The Glassy Ladies, accessible - from - <a - href="https://theglassyladies.com" - rel="external nofollow noopener" - target="_blank" - >https://theglassyladies.com</a - > - </p> - </li> - <li> - <p> - <strong>You</strong> means the individual accessing or using the - Service, or the company, or other legal entity on behalf of which - such individual is accessing or using the Service, as applicable. - </p> - </li> - </ul> - <h1>Collecting and Using Your Personal Data</h1> - <h2>Types of Data Collected</h2> - <h3>Personal Data</h3> - <p> - While using Our Service, We may ask You to provide Us with certain - personally identifiable information that can be used to contact or - identify You. Personally identifiable information may include, but is - not limited to: - </p> - <ul> - <li> - <p>Email address</p> - </li> - <li> - <p>First name and last name</p> - </li> - <li> - <p>Phone number</p> - </li> - <li> - <p>Address, State, Province, ZIP/Postal code, City</p> - </li> - <li> - <p>Usage Data</p> - </li> - </ul> - <h3>Usage Data</h3> - <p>Usage Data is collected automatically when using the Service.</p> - <p> - Usage Data may include information such as Your Device's Internet - Protocol address (e.g. IP address), browser type, browser version, the - pages of our Service that You visit, the time and date of Your visit, - the time spent on those pages, unique device identifiers and other - diagnostic data. - </p> - <p> - When You access the Service by or through a mobile device, We may - collect certain information automatically, including, but not limited - to, the type of mobile device You use, Your mobile device unique ID, - the IP address of Your mobile device, Your mobile operating system, - the type of mobile Internet browser You use, unique device identifiers - and other diagnostic data. - </p> - <p> - We may also collect information that Your browser sends whenever You - visit our Service or when You access the Service by or through a - mobile device. - </p> - <h3>Tracking Technologies and Cookies</h3> - <p> - We do <em>not</em> use Cookies or similar tracking technologies to - track the activity on Our Service and store information. - </p> - <h2>Use of Your Personal Data</h2> - <p>The Company may use Personal Data for the following purposes:</p> - <ul> - <li> - <p> - <strong>To provide and maintain our Service</strong>, including to - monitor the usage of our Service. - </p> - </li> - <li> - <p> - <strong>To manage Your Account:</strong> to manage Your - registration as a user of the Service. The Personal Data You - provide can give You access to different functionalities of the - Service that are available to You as a registered user. - </p> - </li> - <li> - <p> - <strong>For the performance of a contract:</strong> the - development, compliance and undertaking of the purchase contract - for the products, items or services You have purchased or of any - other contract with Us through the Service. - </p> - </li> - <li> - <p> - <strong>To contact You:</strong> To contact You by email, - telephone calls, SMS, or other equivalent forms of electronic - communication, such as a mobile application's push notifications - regarding updates or informative communications related to the - functionalities, products or contracted services, including the - security updates, when necessary or reasonable for their - implementation. - </p> - </li> - <li> - <p> - <strong>To provide You</strong> with news, special offers and - general information about other goods, services and events which - we offer that are similar to those that you have already purchased - or enquired about unless You have opted not to receive such - information. - </p> - </li> - <li> - <p> - <strong>To manage Your requests:</strong> To attend and manage - Your requests to Us. - </p> - </li> - <li> - <p> - <strong>For business transfers:</strong> We may use Your - information to evaluate or conduct a merger, divestiture, - restructuring, reorganization, dissolution, or other sale or - transfer of some or all of Our assets, whether as a going concern - or as part of bankruptcy, liquidation, or similar proceeding, in - which Personal Data held by Us about our Service users is among - the assets transferred. - </p> - </li> - <li> - <p> - <strong>For other purposes</strong>: We may use Your information - for other purposes, such as data analysis, identifying usage - trends, determining the effectiveness of our promotional campaigns - and to evaluate and improve our Service, products, services, - marketing and your experience. - </p> - </li> - </ul> - <p> - We may share Your personal information in the following situations: - </p> - <ul> - <li> - <strong>With Service Providers:</strong> We may share Your personal - information with Service Providers to monitor and analyze the use of - our Service, to contact You. - </li> - <li> - <strong>For business transfers:</strong> We may share or transfer - Your personal information in connection with, or during negotiations - of, any merger, sale of Company assets, financing, or acquisition of - all or a portion of Our business to another company. - </li> - <li> - <strong>With Affiliates:</strong> We may share Your information with - Our affiliates, in which case we will require those affiliates to - honor this Privacy Policy. Affiliates include Our parent company and - any other subsidiaries, joint venture partners or other companies - that We control or that are under common control with Us. - </li> - <li> - <strong>With business partners:</strong> We may share Your - information with Our business partners to offer You certain - products, services or promotions. - </li> - <li> - <strong>With other users:</strong> when You share personal - information or otherwise interact in the public areas with other - users, such information may be viewed by all users and may be - publicly distributed outside. If You interact with other users or - register through a Third-Party Social Media Service, Your contacts - on the Third-Party Social Media Service may see Your name, profile, - pictures and description of Your activity. Similarly, other users - will be able to view descriptions of Your activity, communicate with - You and view Your profile. - </li> - <li> - <strong>With Your consent</strong>: We may disclose Your personal - information for any other purpose with Your consent. - </li> - </ul> - <h2>Retention of Your Personal Data</h2> - <p> - The Company will retain Your Personal Data only for as long as is - necessary for the purposes set out in this Privacy Policy. We will - retain and use Your Personal Data to the extent necessary to comply - with our legal obligations (for example, if we are required to retain - your data to comply with applicable laws), resolve disputes, and - enforce our legal agreements and policies. - </p> - <p> - The Company will also retain Usage Data for internal analysis - purposes. Usage Data is generally retained for a shorter period of - time, except when this data is used to strengthen the security or to - improve the functionality of Our Service, or We are legally obligated - to retain this data for longer time periods. - </p> - <h2>Transfer of Your Personal Data</h2> - <p> - Your information, including Personal Data, is processed at the - Company's operating offices and in any other places where the parties - involved in the processing are located. It means that this information - may be transferred to — and maintained on — computers located outside - of Your state, province, country or other governmental jurisdiction - where the data protection laws may differ than those from Your - jurisdiction. - </p> - <p> - Your consent to this Privacy Policy followed by Your submission of - such information represents Your agreement to that transfer. - </p> - <p> - The Company will take all steps reasonably necessary to ensure that - Your data is treated securely and in accordance with this Privacy - Policy and no transfer of Your Personal Data will take place to an - organization or a country unless there are adequate controls in place - including the security of Your data and other personal information. - </p> - <h2>Disclosure of Your Personal Data</h2> - <h3>Business Transactions</h3> - <p> - If the Company is involved in a merger, acquisition or asset sale, - Your Personal Data may be transferred. We will provide notice before - Your Personal Data is transferred and becomes subject to a different - Privacy Policy. - </p> - <h3>Law enforcement</h3> - <p> - Under certain circumstances, the Company may be required to disclose - Your Personal Data if required to do so by law or in response to valid - requests by public authorities (e.g. a court or a government agency). - </p> - <h3>Other legal requirements</h3> - <p> - The Company may disclose Your Personal Data in the good faith belief - that such action is necessary to: - </p> - <ul> - <li>Comply with a legal obligation</li> - <li>Protect and defend the rights or property of the Company</li> - <li> - Prevent or investigate possible wrongdoing in connection with the - Service - </li> - <li> - Protect the personal safety of Users of the Service or the public - </li> - <li>Protect against legal liability</li> - </ul> - <h2>Security of Your Personal Data</h2> - <p> - The security of Your Personal Data is important to Us, but remember - that no method of transmission over the Internet, or method of - electronic storage is 100% secure. While We strive to use commercially - acceptable means to protect Your Personal Data, We cannot guarantee - its absolute security. - </p> - <h1>Links to Other Websites</h1> - <p> - Our Service may contain links to other websites that are not operated - by Us. If You click on a third party link, You will be directed to - that third party's site. We strongly advise You to review the Privacy - Policy of every site You visit. - </p> - <p> - We have no control over and assume no responsibility for the content, - privacy policies or practices of any third party sites or services. - </p> - <h1>Changes to this Privacy Policy</h1> - <p> - We may update Our Privacy Policy from time to time. We will notify You - of any changes by posting the new Privacy Policy on this page. - </p> - <p> - We will let You know via email and/or a prominent notice on Our - Service, prior to the change becoming effective and update the - "Last updated" date at the top of this Privacy Policy. - </p> - <p> - You are advised to review this Privacy Policy periodically for any - changes. Changes to this Privacy Policy are effective when they are - posted on this page. - </p> - <h1>Contact Us</h1> - <p> - If you have any questions about this Privacy Policy, You can contact - us: - </p> - <ul> - <li>By email: privacy@theglassyladies.com</li> - </ul> - </article> - </section> - </div> -</template> |