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

FEExam PR - Initial commit to submit exam answers. #13

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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 .env.development
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=e01b7895a403fa7364061b2f01a650fc
BACKEND_API_HOST=https://demo.duendesoftware.com
BACKEND_API_HOST=https://new-dev.accelist.com:1234
OIDC_ISSUER=https://demo.duendesoftware.com
OIDC_CLIENT_ID=interactive.public.short
OIDC_SCOPE=openid profile email api offline_access
58 changes: 34 additions & 24 deletions components/DefautLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useState } from "react";
import Head from 'next/head';
import { Avatar, Button, ConfigProvider, Drawer, Layout, Menu, MenuProps } from "antd";
import { faBars, faSignOut, faSignIn, faHome, faCubes, faUser, faUsers, faFlaskVial } from '@fortawesome/free-solid-svg-icons'
import { faBars, faSignOut, faSignIn, faHome, faCubes, faUser, faUsers, faFlaskVial, faPlus } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useRouter } from "next/router";
import { useSession, signIn, signOut } from "next-auth/react";
Expand Down Expand Up @@ -102,36 +102,46 @@ const DefaultLayout: React.FC<{
]
}
);

if (status === 'authenticated') {
menu.push({
key: '/sign-out',
label: 'Sign out',
icon: <FontAwesomeIcon icon={faSignOut}></FontAwesomeIcon>,
onClick: () => {
nProgress.start();
signOut();
// HINT: use this method call if need to end SSO server authentication session:
// signOut({
// callbackUrl: '/api/end-session'
// });
}
key: '/post-order',
label: 'Post Order',
icon: <FontAwesomeIcon icon={faPlus}></FontAwesomeIcon>,
onClick: () => {
nProgress.start();
router.push('/be-orders/orderpostpage');
},
});
} else {

menu.push({
key: '/sign-in',
label: 'Sign in',
icon: <FontAwesomeIcon icon={faSignIn}></FontAwesomeIcon>,
onClick: () => {
nProgress.start();
signIn('oidc');
}
key: '/sign-out',
label: 'Sign out',
icon: <FontAwesomeIcon icon={faSignOut}></FontAwesomeIcon>,
onClick: () => {
nProgress.start();
signOut();
// HINT: use this method call if need to end SSO server authentication session:
// signOut({
// callbackUrl: '/api/end-session'
// });
},
});
} else {
menu.push({
key: '/sign-in',
label: 'Sign in',
icon: <FontAwesomeIcon icon={faSignIn}></FontAwesomeIcon>,
onClick: () => {
nProgress.start();
signIn('oidc');
},
});
}

return menu;
}

return menu;
}

const displayUserName = session?.user?.name;

function renderAvatar() {
Expand Down
27 changes: 26 additions & 1 deletion 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 package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,13 @@
"@fortawesome/react-fontawesome": "0.2.0",
"@hookform/error-message": "2.0.1",
"@hookform/resolvers": "3.0.1",
"@tanstack/react-query": "^5.32.0",
"antd": "5.4.0",
"dayjs": "1.11.7",
"http-proxy": "1.18.1",
"jotai": "2.0.3",
"next": "13.3.0",
"next-auth": "4.22.0",
"next-auth": "^4.22.0",
"nprogress": "0.2.0",
"openid-client": "5.4.0",
"react": "18.2.0",
Expand Down
21 changes: 14 additions & 7 deletions pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Router from 'next/router';
import NProgress from 'nprogress';
import { SessionProvider } from 'next-auth/react';
import { SessionErrorHandler } from '../components/SessionErrorHandler';
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; // Add this line

// https://fontawesome.com/v5/docs/web/use-with/react#next-js
import { config } from '@fortawesome/fontawesome-svg-core';
Expand All @@ -26,16 +27,21 @@ function CustomApp({
Component,
pageProps: { session, ...pageProps }
}: AppPropsWithLayout): JSX.Element {
const queryClient = new QueryClient();

// https://nextjs.org/docs/basic-features/layouts#per-page-layouts
const withLayout = Component.layout ?? (page => page);
return (
// https://next-auth.js.org/getting-started/client#sessionprovider
<SessionProvider session={session}
refetchInterval={120} refetchWhenOffline={false} refetchOnWindowFocus={false}>
<SessionErrorHandler>
{withLayout(<Component {...pageProps} />)}
</SessionErrorHandler>
</SessionProvider>
// Wrap your application in the QueryClientProvider
<QueryClientProvider client={queryClient}>
{/* https://next-auth.js.org/getting-started/client#sessionprovider */}
<SessionProvider session={session}
refetchInterval={120} refetchWhenOffline={false} refetchOnWindowFocus={false}>
<SessionErrorHandler>
{withLayout(<Component {...pageProps} />)}
</SessionErrorHandler>
</SessionProvider>
</QueryClientProvider>
);
}

Expand All @@ -48,3 +54,4 @@ Router.events.on('routeChangeComplete', NProgress.done);
Router.events.on('routeChangeError', NProgress.done);

export default CustomApp;

38 changes: 38 additions & 0 deletions pages/be-orders/orderdeletepage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { useRouter } from 'next/router';
import { WithDefaultLayout } from '@/components/DefautLayout';

export default function DeleteOrderPage() {
const router = useRouter();
const { id } = router.query; //You get this from the URL, which you get from the MainMenu.

const handleDelete = async () => {
const confirmDelete = window.confirm('Are you sure you want to delete this order?');
if (!confirmDelete) {
return;
}

// Make a DELETE request to your API
const response = await fetch(`api/be/api/v1/Order/DeleteOrder/${id}`, {
method: 'DELETE',
});

if (response.ok) {
// If the order was successfully deleted, redirect to the main menu
router.push('/MainMenu');
} else {
// Handle error
alert('Failed to delete order');
}
};

return (
<div className="p-6 bg-white shadow rounded-lg">
<h2 className="text-2xl font-bold mb-4 text-gray-800">Delete Order</h2>
<button className="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline" onClick={handleDelete}>
Delete Order
</button>
</div>
);
}

DeleteOrderPage.layout = WithDefaultLayout;
58 changes: 58 additions & 0 deletions pages/be-orders/orderdetailpage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { useState, useEffect } from 'react';
import { useRouter } from 'next/router';
import { WithDefaultLayout } from '@/components/DefautLayout';

export default function OrderDetailPage() {
const router = useRouter();
const { id } = router.query;
const [order, setOrder] = useState<Order | null>(null);

interface Order {
orderId: number;
description: string;
orderFrom: string;
orderTo: string;
total: number;
quantity: number;
orderedAt: string;
}

useEffect(() => {
const fetchOrder = async () => {
const response = await fetch(`api/be/api/v1/Order/OrderDetail/${id}`);
const data = await response.json();
setOrder(data);
};

if (id) {
fetchOrder();
}
}, [id]);

if (!order) {
return null;
}

return (
<div className="p-6 bg-white shadow rounded-lg">
<h2 className="text-2xl font-bold mb-4 text-gray-800">Order Detail</h2>
<div className="grid grid-cols-2 gap-4 text-gray-600">
<div className="font-semibold">Order ID:</div>
<div>{order.orderId}</div>
<div className="font-semibold">Description:</div>
<div>{order.description}</div>
<div className="font-semibold">Order From:</div>
<div>{order.orderFrom}</div>
<div className="font-semibold">Order To:</div>
<div>{order.orderTo}</div>
<div className="font-semibold">Ordered At:</div>
<div>{order.orderedAt}</div>
<div className="font-semibold">Quantity:</div>
<div>{order.quantity}</div>
</div>
</div>
);
}

OrderDetailPage.layout = WithDefaultLayout;

110 changes: 110 additions & 0 deletions pages/be-orders/orderpostpage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { useState, FormEvent } from 'react';
import { useRouter } from 'next/router';
import { WithDefaultLayout } from '@/components/DefautLayout';

export default function PostOrderPage() {
const [orderFrom, setOrderFrom] = useState<string>('');
const [orderTo, setOrderTo] = useState<string>('');
const [total, setTotal] = useState<string>('');
const [quantity, setQuantity] = useState<number>(1);
const [orderedAt, setOrderedAt] = useState<string>('');
const router = useRouter();

const handleSubmit = async (e: FormEvent) => {
e.preventDefault();

// Add your validation logic here
if (!orderFrom || orderFrom.length < 1) {
alert('Invalid order from');
return;
}
if (!orderTo || orderTo.length < 1) {
alert('Invalid order to');
return;
}
if (!total || isNaN(Number(total))) {
alert('Invalid total');
return;
}
if (!quantity || quantity < 1 || quantity > 99) {
alert('Invalid quantity');
return;
}
if (!orderedAt || new Date(orderedAt) < new Date()) {
alert('Invalid ordered at date');
return;
}

// Make a POST request to your API
const response = await fetch('api/be/api/v1/Order/CreateOrder', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
orderFrom: orderFrom,
orderTo: orderTo,
total: total,
quantity: quantity,
orderedAt: orderedAt,
}),
});

if (response.ok) {
// If the order was successfully posted, redirect to the main menu
router.push('/MainMenu');
} else {
// Handle error
alert('Failed to post order');
}
};

return (
<div className="flex flex-col items-center justify-center min-h-screen py-2">
<div className="max-w-md w-full space-y-8">
<h2 className="mt-6 text-center text-2xl font-bold text-gray-900">Post Order</h2>
<form onSubmit={handleSubmit} className="mt-8 space-y-6">
<div className="rounded-md shadow-sm -space-y-px">
<div>
<label>Order From:</label>
<input type="text" value={orderFrom} onChange={(e) => setOrderFrom(e.target.value)} required className="appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-300 text-gray-900 rounded-t-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm"/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700">Order To:</label>
<input type="text" value={orderTo} onChange={(e) => setOrderTo(e.target.value)} required className="mt-1 block w-full py-2 px-3 border border-gray-300 bg-white rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm" />
</div>
<div>
<label className="block text-sm font-medium text-gray-700">Total:</label>
<input type="number" value={total} onChange={(e) => setTotal(e.target.value)} required className="mt-1 block w-full py-2 px-3 border border-gray-300 bg-white rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm" />
</div>
<div>
<label className="block text-sm font-medium text-gray-700">Quantity:</label>
<input
type="number"
min="1"
max="99"
value={quantity}
onChange={(e) => setQuantity(Number(e.target.value))}
required
className="mt-1 block w-full py-2 px-3 border border-gray-300 bg-white rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700">Ordered At:</label>
<input type="date" value={orderedAt} onChange={(e) => setOrderedAt(e.target.value)} required className="mt-1 block w-full py-2 px-3 border border-gray-300 bg-white rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm" />
</div>
</div>
<div>
<button type="submit" className="group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
Post Order
</button>
</div>
</form>
</div>
</div>
);
}

PostOrderPage.layout = WithDefaultLayout;


Loading
Loading