mirror of
https://github.com/Fluffy-Bean/TastyBites.git
synced 2025-05-30 07:13:12 +00:00
Increase hit target for dropdown
Add CartRecord type Remove OOP style Promises from Contact Add contact reason dropdown The usual SCSS changes and adjustments
This commit is contained in:
parent
865654de87
commit
c908a544aa
7 changed files with 125 additions and 51 deletions
|
@ -6,12 +6,10 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="dropdown" class:open={open}>
|
<div class="dropdown" class:open={open}>
|
||||||
<div class="dropdown-header">
|
<button class="dropdown-header" on:click={() => { open = !open }}>
|
||||||
<p>{name}</p>
|
<span class="dropdown-text">{name}</span>
|
||||||
<button on:click={() => { open = !open }}>
|
<span class="dropdown-button"><CaretDown /></span>
|
||||||
<CaretDown />
|
</button>
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="dropdown-content">
|
<div class="dropdown-content">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
|
@ -28,6 +26,8 @@
|
||||||
.dropdown-header {
|
.dropdown-header {
|
||||||
padding: $spacing-normal;
|
padding: $spacing-normal;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -35,12 +35,15 @@
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
p {
|
border: 0 solid transparent;
|
||||||
|
background-color: transparent;
|
||||||
|
|
||||||
|
.dropdown-text {
|
||||||
font-size: $font-size-h6;
|
font-size: $font-size-h6;
|
||||||
font-weight: $font-weight-bold;
|
font-weight: $font-weight-bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
.dropdown-button {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
@ -62,9 +65,13 @@
|
||||||
color: $color-on-light;
|
color: $color-on-light;
|
||||||
|
|
||||||
transition: transform 0.1s ease-in-out;
|
transition: transform 0.1s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover, &:focus-visible {
|
||||||
|
.dropdown-button {
|
||||||
color: $color-primary;
|
color: $color-primary;
|
||||||
|
outline: 0 solid transparent;
|
||||||
|
transform: rotate(-15deg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,11 +83,15 @@
|
||||||
|
|
||||||
&.open {
|
&.open {
|
||||||
.dropdown-header {
|
.dropdown-header {
|
||||||
//padding: $spacing-normal $spacing-normal $spacing-small;
|
.dropdown-button {
|
||||||
|
|
||||||
button {
|
|
||||||
transform: rotate(-180deg);
|
transform: rotate(-180deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:hover, &:focus-visible {
|
||||||
|
.dropdown-button {
|
||||||
|
transform: rotate(calc(-180deg + 15deg));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.dropdown-content {
|
.dropdown-content {
|
||||||
display: block;
|
display: block;
|
||||||
|
|
|
@ -1,24 +1,23 @@
|
||||||
import { type Writable, get, writable } from "svelte/store";
|
import { type Writable, get, writable } from "svelte/store";
|
||||||
|
|
||||||
import { type CartItem, type Item } from "./types";
|
import { type CartRecord, type CartItem, type Item } from "./types";
|
||||||
import { getItemByUUID, postVerifyCart } from "./test-api";
|
import { getItemByUUID, postVerifyCart } from "./test-api";
|
||||||
|
|
||||||
function createCartStore() {
|
function createCartStore() {
|
||||||
let loaded = false;
|
let loaded = false;
|
||||||
|
|
||||||
const cart: Writable<Record<string, CartItem>> = writable({});
|
const cart: Writable<CartRecord> = writable({});
|
||||||
|
|
||||||
async function init() {
|
async function init(): Promise<void> {
|
||||||
let localData: Record<string, CartItem> = {};
|
let localData: CartRecord = {};
|
||||||
try {
|
try {
|
||||||
localData = JSON.parse(localStorage.getItem("basket")) || {};
|
localData = JSON.parse(localStorage.getItem("basket")) || {};
|
||||||
} catch {
|
} catch {
|
||||||
console.error("Local Cart data fucked");
|
console.error("Local Cart data could not be parsed");
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const newData: Record<string, CartItem> =
|
const newData: CartRecord = await postVerifyCart(localData);
|
||||||
await postVerifyCart(localData);
|
|
||||||
cart.set(newData);
|
cart.set(newData);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Could not load basket:", error);
|
console.error("Could not load basket:", error);
|
||||||
|
@ -28,18 +27,16 @@ function createCartStore() {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function addToCart(uuid: string, amount: number) {
|
async function addToCart(uuid: string, amount: number) {
|
||||||
if (!loaded) {
|
if (!loaded) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (get(cart)[uuid] !== undefined) {
|
if (get(cart)[uuid] !== undefined) {
|
||||||
cart.update((cart: Record<string, CartItem>) => {
|
cart.update((cart: CartRecord) => {
|
||||||
cart[uuid].amount += amount;
|
cart[uuid].amount += amount;
|
||||||
return cart;
|
return cart;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
await getItemByUUID(uuid).then((data: Item) => {
|
await getItemByUUID(uuid).then((data: Item) => {
|
||||||
cart.update((cart: Record<string, CartItem>) =>
|
cart.update((cart: CartRecord) =>
|
||||||
Object.assign({}, cart, {
|
Object.assign({}, cart, {
|
||||||
[uuid]: { uuid, amount, data },
|
[uuid]: { uuid, amount, data },
|
||||||
})
|
})
|
||||||
|
@ -47,13 +44,9 @@ function createCartStore() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
cart.update((cart: Record<string, CartItem>) => {
|
cart.update((cart: CartRecord) => {
|
||||||
if (cart[uuid].amount <= 0) {
|
if (cart.uuid.amount <= 0) delete cart.uuid;
|
||||||
delete cart[uuid]; // skipcq: JS-0320
|
if (cart.uuid.amount > 99) cart.uuid.amount = 99;
|
||||||
} else if (cart[uuid].amount > 99) {
|
|
||||||
cart[uuid].amount = 99; // skipcq: JS-0320
|
|
||||||
}
|
|
||||||
|
|
||||||
return cart;
|
return cart;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -82,12 +75,10 @@ function createCartStore() {
|
||||||
return totalCartPrice;
|
return totalCartPrice;
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeByUUID(uuid: string) {
|
function removeByUUID(uuid: string): void {
|
||||||
if (!loaded) {
|
if (!loaded) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
cart.update((cart) => {
|
cart.update((cart: CartRecord) => {
|
||||||
delete cart[uuid]; // skipcq: JS-0320
|
delete cart[uuid]; // skipcq: JS-0320
|
||||||
return cart;
|
return cart;
|
||||||
});
|
});
|
||||||
|
@ -110,7 +101,7 @@ const Cart = createCartStore();
|
||||||
export const cartLoaded = Cart.init();
|
export const cartLoaded = Cart.init();
|
||||||
|
|
||||||
// Make sure to update localstorage on any changes
|
// Make sure to update localstorage on any changes
|
||||||
Cart.subscribe((value) => {
|
Cart.subscribe((value: CartRecord) => {
|
||||||
localStorage.setItem("basket", JSON.stringify(value));
|
localStorage.setItem("basket", JSON.stringify(value));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -71,12 +71,14 @@ export async function getItemByUUID(uuid: string): Promise<Item> {
|
||||||
export async function postContactEmail(
|
export async function postContactEmail(
|
||||||
name: string,
|
name: string,
|
||||||
email: string,
|
email: string,
|
||||||
|
reason: string,
|
||||||
message: string
|
message: string
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
await fakeDelay(200);
|
await fakeDelay(200);
|
||||||
|
|
||||||
if (!name) throw new Error("Name missing");
|
if (!name) throw new Error("Name missing");
|
||||||
if (!email) throw new Error("Email missing");
|
if (!email) throw new Error("Email missing");
|
||||||
|
if (!reason) throw new Error("Reason missing");
|
||||||
if (!message) throw new Error("Message missing");
|
if (!message) throw new Error("Message missing");
|
||||||
if (message.length < 150) throw new Error("Message FUCKED");
|
if (message.length < 150) throw new Error("Message FUCKED");
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,8 @@ export type CartItem = {
|
||||||
data: Item;
|
data: Item;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type CartRecord = Record<string, CartItem>
|
||||||
|
|
||||||
export type JSONResponse = {
|
export type JSONResponse = {
|
||||||
data?: {
|
data?: {
|
||||||
item: Item[]; // Todo Make this not just item type
|
item: Item[]; // Todo Make this not just item type
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { PaperPlaneRight, SealWarning, SealCheck } from "phosphor-svelte";
|
import { PaperPlaneRight, SealWarning, SealCheck, CaretDown } from "phosphor-svelte";
|
||||||
|
|
||||||
import { postContactEmail } from "../lib/test-api";
|
import { postContactEmail } from "../lib/test-api";
|
||||||
import { expandOnTyping } from "../lib/utils";
|
import { expandOnTyping } from "../lib/utils";
|
||||||
|
@ -11,27 +11,28 @@
|
||||||
|
|
||||||
let name = "";
|
let name = "";
|
||||||
let email = "";
|
let email = "";
|
||||||
|
let reason = "";
|
||||||
let message = "";
|
let message = "";
|
||||||
|
|
||||||
let nameValid = true;
|
let nameValid = true;
|
||||||
let emailValid = true;
|
let emailValid = true;
|
||||||
|
let reasonValid = true;
|
||||||
let messageValid = false;
|
let messageValid = false;
|
||||||
|
|
||||||
function validateName() { nameValid = name.length > 1 }
|
function validateName() { nameValid = name.length > 1 }
|
||||||
function validateEmail() { emailValid = email.length > 1 }
|
function validateEmail() { emailValid = email.length > 1 }
|
||||||
|
function validateReason() { reasonValid = reason != "" }
|
||||||
function validateMessage() { messageValid = message.length > minMessageLength }
|
function validateMessage() { messageValid = message.length > minMessageLength }
|
||||||
|
|
||||||
function onSubmit() {
|
function onSubmit() {
|
||||||
nameValid = true;
|
try {
|
||||||
emailValid = true;
|
formMessage = postContactEmail(name, email, reason, message)
|
||||||
|
} catch (error) {
|
||||||
formMessage = postContactEmail(name, email, message)
|
validateName();
|
||||||
.catch((error) => {
|
validateEmail();
|
||||||
validateName();
|
validateReason();
|
||||||
validateEmail();
|
validateMessage();
|
||||||
validateMessage();
|
}
|
||||||
throw error;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -96,13 +97,41 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="spacer half" />
|
<div class="spacer half" />
|
||||||
<!-- ToDo: Add dropdown for issue type, such as Technical, Order, Account, or other -->
|
|
||||||
|
<div class="form-element">
|
||||||
|
<label class="form-label" for="reason">Contact Reason</label>
|
||||||
|
<div class="select-container">
|
||||||
|
<select
|
||||||
|
bind:value={reason}
|
||||||
|
on:blur={validateReason}
|
||||||
|
on:input={validateReason}
|
||||||
|
class="form-input"
|
||||||
|
id="reason"
|
||||||
|
name="reason"
|
||||||
|
>
|
||||||
|
<option value="general">General Enquiry</option>
|
||||||
|
<option value="technical">Technical/Website</option>
|
||||||
|
<option value="order">Order</option>
|
||||||
|
<option value="booking">Booking</option>
|
||||||
|
</select>
|
||||||
|
<div class="select-arrow">
|
||||||
|
<CaretDown />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span class="form-notice error">
|
||||||
|
{#if !reasonValid}
|
||||||
|
You must provide a reason for contact
|
||||||
|
{/if}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="spacer half" />
|
||||||
|
|
||||||
<div class="form-element">
|
<div class="form-element">
|
||||||
<label class="form-label" for="message">Message</label>
|
<label class="form-label" for="message">Message</label>
|
||||||
<textarea
|
<textarea
|
||||||
bind:value={message}
|
bind:value={message}
|
||||||
on:input={validateMessage}
|
on:blur={validateMessage}
|
||||||
use:expandOnTyping
|
use:expandOnTyping
|
||||||
rows="1"
|
rows="1"
|
||||||
cols="50"
|
cols="50"
|
||||||
|
@ -146,6 +175,37 @@
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.select-container{
|
||||||
|
width: max-content;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
> select {
|
||||||
|
width: 200px;
|
||||||
|
appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .select-arrow {
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: $spacing-small;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
transition: transform 0.1s ease-in-out;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
> .select-arrow {
|
||||||
|
transform: translateY(2px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
padding: 0 $spacing-normal;
|
padding: 0 $spacing-normal;
|
||||||
|
|
||||||
|
|
|
@ -170,6 +170,10 @@
|
||||||
margin-left: $spacing-normal;
|
margin-left: $spacing-normal;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
|
> h2, p {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#filter {
|
#filter {
|
||||||
|
@ -222,6 +226,10 @@
|
||||||
#menu-list {
|
#menu-list {
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
margin-top: $spacing-normal;
|
margin-top: $spacing-normal;
|
||||||
|
|
||||||
|
> h2, p {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
font-family: $font-family;
|
font-family: $font-family;
|
||||||
font-size: $font-size-p;
|
font-size: $font-size-p;
|
||||||
|
|
||||||
border: 1px solid rgba($color-dark, 0.2);
|
border: 1px solid $color-light;
|
||||||
border-radius: $border-radius-normal;
|
border-radius: $border-radius-normal;
|
||||||
background-color: $color-light;
|
background-color: $color-light;
|
||||||
color: $color-on-light;
|
color: $color-on-light;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue