Skip to content
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: 2 additions & 0 deletions appsettings.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@ module.exports = {
oidcIssuer: process.env['OIDC_ISSUER'] ?? '',
oidcClientId: process.env['OIDC_CLIENT_ID'] ?? '',
oidcScope: process.env['OIDC_SCOPE'] ?? '',
backendApi: process.env['BACKEND_API'] ?? '',

};
69 changes: 5 additions & 64 deletions components/DefautLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,71 +36,12 @@ const DefaultLayout: React.FC<{

menu.push(
{
key: '#menu-1',
label: 'Menu 1',
key: '/main',
label: 'Main',
icon: <FontAwesomeIcon icon={faCubes}></FontAwesomeIcon>,
children: [
{
key: '/dashboard',
label: 'Dashboard',
onClick: () => router.push('/dashboard')
},
{
key: '/sub-menu-b',
label: 'Sub Menu B',
onClick: () => router.push('/')
},
{
key: '/sub-menu-c',
label: 'Sub Menu C',
onClick: () => router.push('/')
}
]
},
{
key: '#menu-2',
label: 'Menu 2',
icon: <FontAwesomeIcon icon={faUsers}></FontAwesomeIcon>,
children: [
{
key: '/sub-menu-d',
label: 'Sub Menu D',
onClick: () => router.push('/')
},
{
key: '/sub-menu-e',
label: 'Sub Menu E',
onClick: () => router.push('/')
},
{
key: '/sub-menu-f',
label: 'Sub Menu F',
onClick: () => router.push('/')
}
]
},
{
key: '#menu-3',
label: 'Menu 3',
icon: <FontAwesomeIcon icon={faFlaskVial}></FontAwesomeIcon>,
children: [
{
key: '/sub-menu-g',
label: 'Sub Menu G',
onClick: () => router.push('/')
},
{
key: '/sub-menu-h',
label: 'Sub Menu H',
onClick: () => router.push('/')
},
{
key: '/sub-menu-i',
label: 'Sub Menu I',
onClick: () => router.push('/')
}
]
}


);

if (status === 'authenticated') {
Expand All @@ -124,7 +65,7 @@ const DefaultLayout: React.FC<{
icon: <FontAwesomeIcon icon={faSignIn}></FontAwesomeIcon>,
onClick: () => {
nProgress.start();
signIn('oidc');
router.push('/login')
}
});
}
Expand Down
4 changes: 2 additions & 2 deletions pages/api/be/[...apiGateway].ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { AppSettings } from '../../../functions/AppSettings';
// Great way to avoid using CORS and making API calls from HTTPS pages to back-end HTTP servers
// Recommendation for projects in Kubernetes cluster: set target to Service DNS name instead of public DNS name
const server = Proxy.createProxyServer({
target: AppSettings.current.backendApiHost,
target: AppSettings.current.backendApi,
// changeOrigin to support name-based virtual hosting
changeOrigin: true,
xfwd: true,
Expand All @@ -23,7 +23,7 @@ server.on('proxyReq', (proxyReq, req) => {
}
proxyReq.removeHeader('cookie');
// console.log(JSON.stringify(proxyReq.getHeaders(), null, 4));
console.log('API Proxy:', req.url, '-->', AppSettings.current.backendApiHost + urlRewrite);
console.log('API Proxy:', req.url, '-->', AppSettings.current.backendApi+ urlRewrite);
});

const apiGateway = async (req: NextApiRequest, res: NextApiResponse) => {
Expand Down
139 changes: 139 additions & 0 deletions pages/createOrder/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import React, { useState } from "react";
import { WithDefaultLayout } from "@/components/DefautLayout";
import { Page } from "@/types/Page";

const CreateOrderPage: Page = () => {
const [description, setDescription] = useState("");
const [orderFrom, setOrderFrom] = useState("");
const [orderTo, setOrderTo] = useState("");
const [quantity, setQuantity] = useState("");
const [orderAt, setOrderAt] = useState("");
const [errorMessage, setErrorMessage] = useState("");

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

try {
if (!description || !orderFrom || !orderTo || !quantity || !orderAt) {
throw new Error("Please fill in all fields.");
}

if (description.length > 100) {
throw new Error("Description must be maximum 100 characters long.");
}

const parsedQuantity = parseInt(quantity);
if (isNaN(parsedQuantity) || parsedQuantity <= 0) {
throw new Error("Quantity must be a positive number.");
}

if (!isValidDate(orderAt)) {
throw new Error(
"Invalid date format for Order At. Please use YYYY-MM-DD format."
);
}

const response = await fetch("/api/be/api/v1/Order/CreateOrder", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
description,
orderFrom,
orderTo,
quantity: parsedQuantity,
orderAt,
}),
});

if (!response.ok) {
throw new Error("Failed to add product.");
}

// Reset form fields and error message on successful submission
setDescription("");
setOrderFrom("");
setOrderTo("");
setQuantity("");
setOrderAt("");
setErrorMessage("");
} catch (error) {
if (error instanceof Error) {
setErrorMessage(error.message);
} else {
setErrorMessage("An unknown error occurred.");
}
}
};

const isValidDate = (dateString: string): boolean => {
const regex = /^\d{4}-\d{2}-\d{2}$/;
return regex.test(dateString);
};

return (
<div className="min-h-screen flex justify-center items-center bg-gray-200">
<div className="w-full max-w-md bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4">
<h2 className="text-2xl font-bold mb-4">Add Product</h2>
<form onSubmit={handleSubmit}>
<div className="mb-4">
<label className="block mb-1">Description:</label>
<input
type="text"
value={description}
onChange={(e) => setDescription(e.target.value)}
maxLength={100}
className="w-full border border-gray-300 rounded px-3 py-2"
/>
</div>
<div className="mb-4">
<label className="block mb-1">Order From:</label>
<input
type="text"
value={orderFrom}
onChange={(e) => setOrderFrom(e.target.value)}
className="w-full border border-gray-300 rounded px-3 py-2"
/>
</div>
<div className="mb-4">
<label className="block mb-1">Order To:</label>
<input
type="text"
value={orderTo}
onChange={(e) => setOrderTo(e.target.value)}
className="w-full border border-gray-300 rounded px-3 py-2"
/>
</div>
<div className="mb-4">
<label className="block mb-1">Quantity:</label>
<input
type="text"
value={quantity}
onChange={(e) => setQuantity(e.target.value)}
className="w-full border border-gray-300 rounded px-3 py-2"
/>
</div>
<div className="mb-4">
<label className="block mb-1">Order At:</label>
<input
type="date"
value={orderAt}
onChange={(e) => setOrderAt(e.target.value)}
className="w-full border border-gray-300 rounded px-3 py-2"
/>
</div>
<button
type="submit"
className="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600"
>
Add Product
</button>
</form>
{errorMessage && <p className="text-red-500 mt-4">{errorMessage}</p>}
</div>
</div>
);
};
CreateOrderPage.layout = WithDefaultLayout;
export default CreateOrderPage;
89 changes: 89 additions & 0 deletions pages/login/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import React from "react";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { useRouter } from "next/router";

const schema = z.object({
email: z.string().nonempty(),
password: z.string().min(4, "Password at least 4 characters"),
});

type FormData = z.infer<typeof schema>;

const LoginPage: React.FC = () => {
const {
register,
handleSubmit,
formState: { errors },
} = useForm<FormData>({
resolver: zodResolver(schema),
});
const router = useRouter();

const onSubmit = async (data: FormData) => {
try {
const response = await fetch("/api/be/api/v1/Auth/Login", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
});

if (!response.ok) {
throw new Error("Login failed. Please check your credentials.");
}
console.log("Login successful!");
} catch (error: any) {
console.error("Login error:", error.message);
}
};

return (
<div className="min-h-screen flex justify-center items-center bg-gray-200">
<div className="w-full max-w-md bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4">
<h2 className="text-2xl font-bold mb-4">Log In</h2>
<form onSubmit={handleSubmit(onSubmit)}>
<div className="mb-4">
<label className="block mb-1">Email:</label>
<input
type="text"
{...register("email")}
className="w-full border border-gray-300 rounded px-3 py-2"
/>
{errors.email && (
<p className="text-red-500 mt-1">{errors.email.message}</p>
)}
</div>
<div className="mb-4">
<label className="block mb-1">Password:</label>
<input
type="password"
{...register("password")}
className="w-full border border-gray-300 rounded px-3 py-2"
/>
{errors.password && (
<p className="text-red-500 mt-1">{errors.password.message}</p>
)}
</div>
<button
onClick={() => router.push("/main")}
type="submit"
className="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600"
>
Log In
</button>
</form>
<p className="text-sm text-gray-600 mt-4">
No Account ?{" "}
<a href="../register" className="text-blue-500 hover:text-blue-700">
Register here
</a>
</p>
</div>
</div>
);
};

export default LoginPage;
Loading