Skip to content
This repository has been archived by the owner on Oct 31, 2024. It is now read-only.

Commit

Permalink
feat(theme): My Account Page (#272, #32)
Browse files Browse the repository at this point in the history
  • Loading branch information
mkucmus authored and patzick committed Jan 15, 2020
1 parent 45b100e commit 6e373a3
Show file tree
Hide file tree
Showing 16 changed files with 576 additions and 7 deletions.
33 changes: 33 additions & 0 deletions packages/composables/__tests__/useUser.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,5 +142,38 @@ describe("Composables - useUser", () => {
expect(error.value).toEqual("Something wrong with logout");
});
});

describe("loadOrders", () => {
it("should load customer's orders from different endpoint", async () => {
const ordersResponse = [
{
id: "12345",
orderNumber: "100123"
}
];
mockedApiClient.getCustomerOrders.mockResolvedValueOnce(
ordersResponse as any
);
const { orders, loadOrders } = useUser();
expect(orders.value).toBeNull();
await loadOrders();
expect(orders.value).toHaveLength(1);
});
});

describe("getOrderDetails", () => {
it("should return order details for given orderId", async () => {
const orderResponse = {
id: "12345",
orderNumber: "100123"
};
mockedApiClient.getCustomerOrderDetails.mockResolvedValueOnce(
orderResponse as any
);
const { getOrderDetails } = useUser();
const orderDetails = await getOrderDetails("12345");
expect(orderDetails).toBe(orderResponse);
});
});
});
});
23 changes: 21 additions & 2 deletions packages/composables/src/hooks/useUser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ import { ref, Ref, computed } from "@vue/composition-api";
import {
login as apiLogin,
logout as apiLogout,
getCustomer
getCustomer,
getCustomerOrders,
getCustomerOrderDetails
} from "@shopware-pwa/shopware-6-client";
import { Customer } from "packages/shopware-6-client/src/interfaces/models/checkout/customer/Customer";
import { getStore } from "@shopware-pwa/composables";
import { Order } from "@shopware-pwa/shopware-6-client/src/interfaces/models/checkout/order/Order";

interface UseUser {
login: ({
Expand All @@ -16,17 +19,21 @@ interface UseUser {
password?: string;
}) => Promise<boolean>;
user: Ref<Customer | null>;
orders: Ref<Order[] | null>;
loading: Ref<boolean>;
error: Ref<any>;
isLoggedIn: Ref<boolean>;
refreshUser: () => Promise<void>;
logout: () => Promise<void>;
loadOrders: () => Promise<void>;
getOrderDetails: (orderId: string) => Promise<Order>;
}

export const useUser = (): UseUser => {
let vuexStore = getStore();
const loading: Ref<boolean> = ref(false);
const error: Ref<any> = ref(null);
const orders: Ref<Order[] | null> = ref(null);

const user: any = computed(() => {
return vuexStore.getters.getUser;
Expand Down Expand Up @@ -65,6 +72,15 @@ export const useUser = (): UseUser => {
vuexStore.commit("SET_USER", user);
};

const loadOrders = async (): Promise<void> => {
const fetchedOrders = await getCustomerOrders();
orders.value = fetchedOrders;
};

const getOrderDetails = async (orderId: string): Promise<Order> => {
return getCustomerOrderDetails(orderId);
};

const isLoggedIn = computed(() => !!user.value);

return {
Expand All @@ -74,6 +90,9 @@ export const useUser = (): UseUser => {
loading,
isLoggedIn,
refreshUser,
logout
logout,
orders,
loadOrders,
getOrderDetails
};
};
2 changes: 1 addition & 1 deletion packages/default-theme/components/TopNavigation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ export default {
)
},
async userIconClick() {
if (this.isLoggedIn) this.logout()
if (this.isLoggedIn) this.$router.push("account")
else this.isModalOpen = true
}
}
Expand Down
71 changes: 71 additions & 0 deletions packages/default-theme/components/account/Address.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<template>
<SfList>
{{address.id}}
<hr/>
<SfProperty
name="name"
:value="name" />
<SfProperty
name="street"
:value="street" />
<SfProperty
name="city"
:value="city" />
<SfProperty
name="zipcode"
:value="zipcode" />
<SfProperty
name="country"
:value="country" />
</SfList>
</template>
<script>
import { SfList, SfProperty } from '@storefront-ui/vue'
import { VuelidateMixin } from '@vuelidate/core'
import { required } from '@vuelidate/validators'
import { useUser } from '@shopware-pwa/composables'
import { CustomerAddress } from '@shopware-pwa/shopware-6-client'
export default {
name: "MyProfile",
mixins: [VuelidateMixin],
components: {SfList, SfProperty},
props: {
address: {
type: CustomerAddress,
default: {}
}
},
computed: {
name(){
return this.address && `${this.address.firstName} ${this.address.lastName}`
},
street() {
return this.address && this.address.street
},
city() {
return this.address && this.address.city
},
zipcode() {
return this.address && this.address.zipcode
},
country() {
return this.address && this.address.country && this.address.country.name || ""
}
},
data() {
return {
}
},
methods: {
}
}
</script>

<style lang="scss" scoped>
@import '~@storefront-ui/vue/styles.scss';
@import '~@storefront-ui/shared/styles/helpers/visibility';
</style>
58 changes: 58 additions & 0 deletions packages/default-theme/components/account/MyProfile.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<template>
<SfTabs :open-tab="1">
<SfTab title="Personal data">
<SfList>
<SfProperty name="customerNumber" :value="customerNumber"/>
<SfProperty name="email" :value="email"/>
<SfProperty name="firstname" :value="firstName"/>
<SfProperty name="lastname" :value="lastName"/>
</SfList>
</SfTab>
</SfTabs>
</template>
<script>
import { SfProperty, SfTabs, SfList } from '@storefront-ui/vue'
import { VuelidateMixin } from '@vuelidate/core'
import { required } from '@vuelidate/validators'
import { useUser } from '@shopware-pwa/composables'
export default {
name: "MyProfile",
mixins: [VuelidateMixin],
components: {SfProperty, SfTabs, SfList},
props: {
},
setup() {
const { user } = useUser()
return {
user
}
},
data() {
return {
}
},
computed: {
customerNumber(){
return this.user && this.user.customerNumber
},
email() {
return this.user && this.user.email
},
firstName(){
return this.user && this.user.firstName
},
lastName(){
return this.user && this.user.lastName
}
},
methods: {
}
}
</script>

<style lang="scss" scoped>
@import '~@storefront-ui/vue/styles.scss';
@import '~@storefront-ui/shared/styles/helpers/visibility';
</style>
47 changes: 47 additions & 0 deletions packages/default-theme/components/account/OrderHistory.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<template>
<div>
<SfList v-for="order in orders" :key="order.id">
<OrderDetails @click="$emit('click')" :order="order" />
<SfDivider/>
</SfList>
</div>

</template>
<script>
import { SfList, SfDivider } from '@storefront-ui/vue'
import { VuelidateMixin } from '@vuelidate/core'
import { required } from '@vuelidate/validators'
import { useUser } from '@shopware-pwa/composables'
import OrderDetails from "./OrderHistory/OrderDetails"
export default {
name: "MyProfile",
mixins: [VuelidateMixin],
components: {SfList, OrderDetails, SfDivider},
props: {
},
setup() {
const { orders, loadOrders } = useUser()
loadOrders()
return {
orders
}
},
data() {
return {
}
},
methods: {
}
}
</script>

<style lang="scss" scoped>
@import '~@storefront-ui/vue/styles.scss';
@import '~@storefront-ui/shared/styles/helpers/visibility';
</style>
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<template>
<div class="order-details__item">
<SfBadge class="sf-badge--full-width"><strong>#{{orderNumber}}</strong></SfBadge>
<SfProperty name="amountTotal" :value="totalAmount"/>
<SfProperty name="status" :value="status"/>
<SfProperty name="orderDateTime" :value="orderDateTime"/>
<SfProperty name="shippingCost" :value="shippingCost"/>

<SfButton v-if="!isLoaded" class="order-details__item__btn sf-button--outline" v-on:click="loadOrderDetails(order.id)">
See more
</SfButton>
{{ orderDetails }}
</div>

</template>
<script>
import { SfProperty, SfButton, SfBadge } from '@storefront-ui/vue'
import { VuelidateMixin } from '@vuelidate/core'
import { required } from '@vuelidate/validators'
import { useUser } from '@shopware-pwa/composables'
import { Order } from '@shopware-pwa/shopware-6-client'
export default {
name: "OrderDetail",
mixins: [VuelidateMixin],
components: {SfButton, SfProperty},
props: {
order: {
type: Order,
default: null
}
},
setup() {
const { getOrderDetails } = useUser()
return {
getOrderDetails
}
},
data() {
return {
orderDetails: null
}
},
computed: {
isLoaded() {
return !!this.orderDetails
},
orderNumber() {
return this.order && this.order.orderNumber
},
totalAmount() {
return this.order && this.order.amountTotal
},
shippingCost() {
return this.order && this.order.shippingCosts && this.order.shippingCosts.totalPrice
},
orderDateTime() {
return this.order && this.order.orderDateTime
},
status() {
return this.order.stateMachineState && this.order.stateMachineState && this.order.stateMachineState.name
}
},
methods: {
// detailed info are available through the separated endpoint
async loadOrderDetails(orderId) {
this.orderDetails = "details"
// uncomment this line once the way of fetching the details is cleared
//orderDetails = await this.getOrderDetails(orderId)
}
}
}
</script>

<style lang="scss" scoped>
@import '~@storefront-ui/vue/styles.scss';
@import '~@storefront-ui/shared/styles/helpers/visibility';
.order-details {
&__item {
padding-top:10px;
padding-bottom:10px;
overflow: auto;
&__btn {
position: relative;
float:right;
}
}
}
</style>
Loading

1 comment on commit 6e373a3

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deploy preview for website ready!

Built with commit 9843462

https://shopware-pwa-njmac5loh.now.sh

Please sign in to comment.