Skip to content

Commit

Permalink
finishes app!
Browse files Browse the repository at this point in the history
  • Loading branch information
DuyKim committed Jun 18, 2023
1 parent cac7400 commit 4d7a6d7
Show file tree
Hide file tree
Showing 13 changed files with 223 additions and 34 deletions.
12 changes: 3 additions & 9 deletions auth/src/routes/__test__/current-user.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,11 @@ test('should response with details about the current user', async () => {
expect(response.body.currentUser.email).toEqual('test@example.com');
});

test('should response with an error if not authenticated', async () => {
test('should response null if not authenticated', async () => {
const response = await request(app)
.get('/api/users/currentuser')
.send()
.expect(401);
.expect(200);

expect(response.body).toMatchInlineSnapshot(`
[
{
"message": "Not authorized",
},
]
`);
expect(response.body.currentUser).toBeNull();
});
6 changes: 3 additions & 3 deletions auth/src/routes/current-user.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import express from 'express';

import { currentUser, requireAuth } from '@asdfkai/common';
import { currentUser } from '@asdfkai/common';

const router = express.Router();

router.get('/api/users/currentuser', currentUser, requireAuth, (req, res) => {
router.get('/api/users/currentuser', currentUser, (req, res) => {
res.send({
currentUser: req.currentUser,
currentUser: req.currentUser ?? null,
});
});

Expand Down
8 changes: 7 additions & 1 deletion client/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
"eslint-config-next": "13.4.4",
"next": "13.4.4",
"react": "18.2.0",
"react-dom": "18.2.0"
"react-dom": "18.2.0",
"react-stripe-checkout": "^2.6.3"
},
"devDependencies": {
"eslint-config-prettier": "^8.8.0"
Expand Down
2 changes: 2 additions & 0 deletions client/src/components/header.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ export default function Header({ currentUser }) {
const links = [
!currentUser && { label: 'Sign up', href: '/auth/signup' },
!currentUser && { label: 'Sign in', href: '/auth/signin' },
currentUser && { label: 'Sell Tickets', href: '/tickets/new' },
currentUser && { label: 'My Orders', href: '/orders' },
currentUser && { label: 'Sign out', href: '/auth/signout' },
]
.filter(Boolean)
Expand Down
4 changes: 2 additions & 2 deletions client/src/hooks/use-request.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import { useState } from 'react';
export default function useRequest({ url, method, body, onSuccess }) {
const [errors, setErrors] = useState([]);

const doRequest = async () => {
const doRequest = async (props = {}) => {
try {
setErrors([]);
const response = await axios[method](url, body);
const response = await axios[method](url, { ...body, ...props });
if (onSuccess) {
console.log('success', response);
onSuccess(response.data);
Expand Down
9 changes: 3 additions & 6 deletions client/src/pages/_app.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,11 @@ import buildClient from '@/api/build-client';
import Header from '../components/header';

export default function CustomApp({ Component, pageProps, currentUser }) {
// useEffect(() => {
// require('bootstrap/dist/js/bootstrap');
// }, []);

return (
<div>
<Header currentUser={currentUser} />
<main>
<Component {...pageProps} />
<main className="container">
<Component {...pageProps} currentUser={currentUser} />
</main>
<footer />
</div>
Expand All @@ -23,6 +19,7 @@ export default function CustomApp({ Component, pageProps, currentUser }) {

CustomApp.getInitialProps = async (appContext) => {
const appProps = await App.getInitialProps(appContext);

const client = buildClient(appContext.ctx);
const { data } = await client.get('/api/users/currentuser');

Expand Down
41 changes: 30 additions & 11 deletions client/src/pages/index.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,31 @@
import Head from 'next/head';
import Image from 'next/image';
import { Inter } from 'next/font/google';
import client from '@/api/build-client';
import buildClient from '@/api/build-client';
import Link from 'next/link';

const inter = Inter({ subsets: ['latin'] });

export const getServerSideProps = async (context) => {
const { data } = await client(context).get('/api/users/currentuser');
const client = buildClient(context);
const { data } = await client.get('/api/tickets');

return { props: { currentUser: data.currentUser ? data.currentUser : null } };
return { props: { tickets: data } };
};

export default function Home({ currentUser }) {
export default function Home({ currentUser, tickets }) {
const ticketsList = tickets.map((ticket) => {
return (
<tr key={ticket.id}>
<td>{ticket.title}</td>
<td>{ticket.price}</td>
<td>
<Link href={`/tickets/${ticket.id}`}>View</Link>
</td>
</tr>
);
});

return (
<>
<Head>
Expand All @@ -20,13 +34,18 @@ export default function Home({ currentUser }) {
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon.ico" />
</Head>
<main className={`${inter.className}`}>
{currentUser ? (
<h1>You are signed in</h1>
) : (
<h1>You are not signed in</h1>
)}
</main>
<div className={`${inter.className}`}>
<h1>Tickets</h1>
<table className="table">
<thead>
<tr>
<th>Title</th>
<th>Price</th>
</tr>
</thead>
<body>{ticketsList}</body>
</table>
</div>
</>
);
}
56 changes: 56 additions & 0 deletions client/src/pages/orders/[orderId].js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import buildClient from '@/api/build-client';
import useRequest from '@/hooks/use-request';
import { Router } from 'next/router';
import { useEffect } from 'react';
import StripeCheckout from 'react-stripe-checkout';

export default function OrderShow({ order, currentUser }) {
const [timeLeft, setTimeLeft] = useState(0);
const [errorComponent, doRequest] = useRequest({
url: '/api/payments',
method: 'post',
body: {
orderId: order.id,
},
onSuccess: () => Router.push('/orders'),
});

useEffect(() => {
const findTimeLeft = () => {
const msLeft = new Date(order.expiresAt) - new Date();
setTimeLeft(Math.round(msLeft / 1000));
};

findTimeLeft();
const timerId = setInterval(findTimeLeft, 1000);

return () => {
clearInterval(timerId);
};
}, []);

if (timeLeft < 0) {
return <div>Order Expired!</div>;
}

return (
<div>
<p>Time left to pay: {msLeft} seconds</p>
<StripeCheckout
token={({ id }) => doRequest({ token: id })}
stripeKey="pk_test_51NAuhnLTrkqQD856STNCM5EudKDpxKGll2N5oizidAWxkDq3IJiEQyersbzPwMKZrUW7Lmi8sI78qMqa7GZXEXHC00xqYlwNqS"
amount={order.ticket.price * 100}
email={currentUser.email}
/>
{errorComponent}
</div>
);
}

export async function getServerSideProps(context) {
const { orderId } = context.query;
const client = buildClient(context);
const { data } = client.get(`/api/orders/${orderId}`);

return { props: { order: data } };
}
20 changes: 20 additions & 0 deletions client/src/pages/orders/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import buildClient from '@/api/build-client';

export default function OrderIndex({ orders }) {
return (
<ul>
{orders.map((order) => (
<li key={order.id}>
{order.ticket.title} - {order.status}
</li>
))}
</ul>
);
}

export async function getServerSideProps(context) {
const client = buildClient(context);
const { data } = await client.get('/api/orders');

return { props: { orders: data } };
}
33 changes: 33 additions & 0 deletions client/src/pages/tickets/[ticketId].js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import useRequest from '@/hooks/use-request';
import { Router } from 'next/router';

export default function Ticket({ ticket }) {
const [errorComponent, doRequest] = useRequest({
url: '/api/orders',
method: 'post',
body: {
ticketId: ticket.id,
},
onSuccess: (order) =>
Router.push('/orders/[orderId]', `/orders/${order.id}`),
});

return (
<div>
<h1>{ticket.title}</h1>
<h4>Price: {ticket.price}</h4>
{errorComponent}
<button className="btn btn-primary" onClick={() => doRequest()}>
Purchase
</button>
</div>
);
}

export async function getServerSideProps(context) {
const { ticketId } = context.query;
const client = buildClient(context);
const { data } = await client.get('/api/tickets/${ticketId}');

return { props: { ticket: data } };
}
59 changes: 59 additions & 0 deletions client/src/pages/tickets/new.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import useRequest from '@/hooks/use-request';
import { Router } from 'next/router';
import { useState } from 'react';

export default function NewTicket() {
const [title, setTitle] = useState('');
const [price, setPrice] = useState();

const [errorComponent, doRequest] = useRequest({
url: '/api/tickets',
method: 'post',
body: {
title,
price,
},
onSuccess: () => Router.push('/'),
});

const onSubmit = (e) => {
e.preventDefault();
doRequest();
};

const onBlurHandler = (e) => {
const value = parseFloat(price);
setPrice(value.toFixed(2));
};

return (
<div>
<h1>Create a Ticket</h1>
<form onSubmit={onSubmit}>
<div className="mb-3">
<label htmlFor="title">Title</label>
<input
id="title"
type="text"
name="title"
className="form-control"
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
</div>
<div className="mb-3">
<label htmlFor="price">Price</label>
<input
id="price"
type="number"
onBlur={onBlurHandler}
name="price"
className="form-control"
/>
</div>
{errorComponent}
<button className="btn btn-primary">Submit</button>
</form>
</div>
);
}
4 changes: 3 additions & 1 deletion tickets/src/routers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import { Ticket } from '@models/ticket';
const router = express.Router();

router.get('/api/tickets', async (req: Request, res: Response) => {
const tickets = await Ticket.find({});
const tickets = await Ticket.find({
orderId: undefined,
});

res.send(tickets);
});
Expand Down

0 comments on commit 4d7a6d7

Please sign in to comment.