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

Initial scaffolding for store orders page #100

Merged
merged 8 commits into from
Dec 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"plugins": ["prettier", "node", "@typescript-eslint", "import", "jsdoc"],
"rules": {
"prettier/prettier": "error",
"no-unused-vars": "warn",
"no-unused-vars": ["warn", { "varsIgnorePattern": "^_$" }],
"no-console": "error",
"func-names": "off",
"no-process-exit": "off",
Expand Down
6 changes: 3 additions & 3 deletions src/components/common/Typography/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ const v2StandardSizes: Record<V2StandardStyle, Record<V2Size, string>> = {
label: {
large: '1.25rem',
medium: '1.125rem',
small: '1.3125rem',
small: '1rem',
},
body: {
large: '1.125rem',
Expand All @@ -96,7 +96,7 @@ const v2StandardHeights: Record<V2StandardStyle, Record<V2Size, string>> = {
label: {
large: '1.625rem',
medium: '1.5rem',
small: '1.875rem',
small: '1.375rem',
},
body: {
large: '1.5rem',
Expand All @@ -119,7 +119,7 @@ const v2StandardSpacings: Partial<Record<V2StandardStyle, Partial<Record<V2Size,

const v2StandardWeights: Record<V2StandardStyle, number> = {
title: 400,
label: 500,
label: 400,
body: 400,
};

Expand Down
57 changes: 57 additions & 0 deletions src/components/store/OrderCard/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { Typography } from '@/components/common';
import { PublicOrder } from '@/lib/types/apiResponses';
import { OrderStatus } from '@/lib/types/enums';
import { formatDate, formatEventDate } from '@/lib/utils';
import styles from './style.module.scss';

export const orderStatusName: { [_ in OrderStatus]: string } = {
[OrderStatus.FULFILLED]: 'Fulfilled',
[OrderStatus.CANCELLED]: 'Cancelled',
[OrderStatus.PLACED]: 'Placed',
[OrderStatus.PARTIALLY_FULFILLED]: 'Partially Fulfilled',
[OrderStatus.PICKUP_MISSED]: 'Pickup Missed',
[OrderStatus.PICKUP_CANCELLED]: 'Pickup Cancelled',
};

export const orderStatusColor: { [_ in OrderStatus]: string } = {
// Green: Order completed and picked up.
[OrderStatus.FULFILLED]: styles.green,
// Gray: Order completed, no further action needed.
[OrderStatus.CANCELLED]: styles.gray,
// Blue: Order pending.
[OrderStatus.PLACED]: styles.blue,
[OrderStatus.PARTIALLY_FULFILLED]: styles.blue,
// Red: Order pending, requires user action.
[OrderStatus.PICKUP_MISSED]: styles.red,
[OrderStatus.PICKUP_CANCELLED]: styles.red,
};

interface OrderCardProps {
order: PublicOrder;
}

const OrderCard = ({ order }: OrderCardProps) => {
return (
<div className={styles.container}>
<div className={styles.orderInfo}>
<div className={styles.label}>
<Typography variant="label/small">ORDER PLACED</Typography>
<Typography variant="body/large" style={{ fontWeight: 700 }}>
{formatDate(order.orderedAt, true)}
</Typography>
</div>
<div className={styles.label}>
<Typography variant="label/small">PICK UP</Typography>
<Typography variant="body/large" style={{ fontWeight: 700 }}>
{formatEventDate(order.pickupEvent.start, order.pickupEvent.end, true)}
</Typography>
</div>
</div>
<div className={`${styles.orderStatus} ${orderStatusColor[order.status]}`}>
<Typography variant="body/medium">{orderStatusName[order.status]}</Typography>
</div>
</div>
);
};

export default OrderCard;
70 changes: 70 additions & 0 deletions src/components/store/OrderCard/style.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
@use 'src/styles/vars.scss' as vars;

.container {
align-items: center;
background-color: var(--theme-surface-1);
border-radius: 0.5rem;
display: flex;
flex-wrap: wrap;
gap: 3rem;
justify-content: space-between;
padding: 0.5rem 1rem;

.orderInfo {
align-items: flex-start;
align-self: stretch;
display: flex;
gap: 3rem;

.label {
display: flex;
flex-direction: column;
}
}

.orderStatus {
background: vars.$gray-400;
border: 1px solid vars.$disabled;
border-radius: 0.875rem;
color: vars.$disabled;
margin-right: 1rem;
padding: 0.25rem 0.5rem;

&.green {
background: vars.$green-100;
border: 1px solid vars.$success-1;
color: vars.$success-1;
}

&.gray {
background: vars.$gray-400;
border: 1px solid vars.$disabled;
color: vars.$disabled;
}

&.blue {
background: vars.$blue-100;
border: 1px solid vars.$light-primary-2;
color: vars.$light-primary-2;
}

&.red {
background: vars.$scarlet-2;
border: 1px solid vars.$danger-1;
color: vars.$danger-1;
}
}
}

@media screen and (width <= vars.$breakpoint-md) {
.container {
align-items: flex-start;
flex-direction: column;
gap: 1rem;

.orderInfo {
flex-direction: column;
gap: 1rem;
}
}
}
16 changes: 16 additions & 0 deletions src/components/store/OrderCard/style.module.scss.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export type Styles = {
blue: string;
container: string;
gray: string;
green: string;
label: string;
orderInfo: string;
orderStatus: string;
red: string;
};

export type ClassNames = keyof Styles;

declare const styles: Styles;

export default styles;
23 changes: 23 additions & 0 deletions src/components/store/OrdersDisplay/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Typography } from '@/components/common';
import { PublicOrder } from '@/lib/types/apiResponses';
import OrderCard from '../OrderCard';
import styles from './style.module.scss';

interface OrdersDisplayProps {
orders: PublicOrder[];
}

const OrdersDisplay = ({ orders }: OrdersDisplayProps) => {
if (orders.length === 0) {
return <Typography variant="body/large">No orders placed!</Typography>;
}
return (
<div className={styles.container}>
{orders.map(order => (
<OrderCard key={order.uuid} order={order} />
))}
</div>
);
};

export default OrdersDisplay;
5 changes: 5 additions & 0 deletions src/components/store/OrdersDisplay/style.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.container {
display: flex;
flex-direction: column;
gap: 1.5rem;
}
9 changes: 9 additions & 0 deletions src/components/store/OrdersDisplay/style.module.scss.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export type Styles = {
container: string;
};

export type ClassNames = keyof Styles;

declare const styles: Styles;

export default styles;
2 changes: 2 additions & 0 deletions src/components/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@ export { default as Diamonds } from './Diamonds';
export { default as HelpModal } from './HelpModal';
export { default as Hero } from './Hero';
export { default as ItemCard } from './ItemCard';
export { default as OrderCard } from './OrderCard';
export { default as OrdersDisplay } from './OrdersDisplay';
export { default as Navbar } from './StoreNavbar';
20 changes: 18 additions & 2 deletions src/lib/api/StoreAPI.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
/* eslint-disable import/prefer-default-export */
import { config } from '@/lib';
import { GetAllMerchCollectionsResponse, PublicMerchCollection } from '@/lib/types/apiResponses';
import type {
GetAllMerchCollectionsResponse,
GetMerchOrdersResponse,
PublicMerchCollection,
PublicOrder,
} from '@/lib/types/apiResponses';
import axios from 'axios';

export const getAllCollections = async (token: string): Promise<PublicMerchCollection[]> => {
Expand All @@ -14,3 +18,15 @@ export const getAllCollections = async (token: string): Promise<PublicMerchColle

return response.data.collections;
};

export const getAllOrders = async (token: string): Promise<PublicOrder[]> => {
const requestUrl = `${config.api.baseUrl}${config.api.endpoints.store.orders}`;

const response = await axios.get<GetMerchOrdersResponse>(requestUrl, {
headers: {
Authorization: `Bearer ${token}`,
},
});

return response.data.orders;
};
3 changes: 2 additions & 1 deletion src/lib/types/apiResponses.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
FeedbackStatus,
FeedbackType,
OrderPickupEventStatus,
OrderStatus,
UserAccessType,
UserState,
} from './enums';
Expand Down Expand Up @@ -218,7 +219,7 @@ export interface PublicOrder {
uuid: UUID;
user: PublicProfile;
totalCost: number;
status: string;
status: OrderStatus;
orderedAt: Date;
pickupEvent: PublicOrderPickupEvent;
}
Expand Down
24 changes: 21 additions & 3 deletions src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,16 +80,34 @@ export const getUserRank = (user: PublicProfile): string => {
*/
export const isSrcAGif = (src: string): boolean => /\.gif($|&)/.test(src);

const rangeFormat = new Intl.DateTimeFormat('en-US', {
const dateFormat = new Intl.DateTimeFormat('en-US', {
month: 'short',
day: 'numeric',
hour: 'numeric',
minute: '2-digit',
});

export const formatEventDate = (start: string, end: string): string => {
const dateFormatWithYear = new Intl.DateTimeFormat('en-US', {
year: 'numeric',
month: 'short',
day: 'numeric',
hour: 'numeric',
minute: '2-digit',
});

export const formatDate = (date: Date | string, year?: boolean): string => {
const format = year ? dateFormatWithYear : dateFormat;
return format.format(new Date(date));
};

export const formatEventDate = (
start: Date | string,
end: Date | string,
year?: boolean
): string => {
const format = year ? dateFormatWithYear : dateFormat;
try {
return rangeFormat.formatRange(new Date(start), new Date(end));
return format.formatRange(new Date(start), new Date(end));
} catch {
return `Invalid date range: ${new Date(start)}, ${new Date(end)}`;
}
Expand Down
Loading