diff --git a/.github/workflows/blank1.yml b/.github/workflows/blank1.yml index 01502b13..e2c58b00 100644 --- a/.github/workflows/blank1.yml +++ b/.github/workflows/blank1.yml @@ -6,9 +6,9 @@ name: CI on: # Triggers the workflow on push or pull request events but only for the "main" branch push: - branches: [ "main" ] + branches: ['main'] pull_request: - branches: [ "main" ] + branches: ['main'] # Allows you to run this workflow manually from the Actions tab workflow_dispatch: diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 00000000..a530b3d5 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,2 @@ +bun run lint +bun run prettier diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..e8462529 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,7 @@ +{ + "semi": true, + "singleQuote": true, + "tabWidth": 2, + "bracketSameLine": false, + "bracketSpacing": true +} diff --git a/Providers.tsx b/Providers.tsx index 3b923afb..acde1e22 100644 --- a/Providers.tsx +++ b/Providers.tsx @@ -1,16 +1,16 @@ -"use client"; -import { Toaster } from "react-hot-toast"; +'use client'; +import { Toaster } from 'react-hot-toast'; -import React from "react"; +import React from 'react'; const Providers = ({ children }: { children: React.ReactNode }) => { return ( <> @@ -19,4 +19,4 @@ const Providers = ({ children }: { children: React.ReactNode }) => { ); }; -export default Providers; \ No newline at end of file +export default Providers; diff --git a/README.md b/README.md index 2ca49736..6e3b406c 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,6 @@ As the above image shows, we documented each our test example in terms of test I ![errors in testing script](https://github.com/Kuzma02/Electronics-eCommerce-Shop-With-Admin-Dashboard-NextJS-NodeJS/assets/138793624/507fa099-2039-47ce-a38b-209166a8d5c4) -

During the software testing process, we documented each error found in the error report form. As shown in the image above, each error has its own unique error ID and a detailed description of the error containing: date of identifying an error, date of troubleshooting an error, error priority, type of error, file name, testing phase.

6.1. Ad hoc testing

@@ -108,7 +107,6 @@ We have applied this method by examining the code after each new added functiona

Is Next.js good for eCommerce?

Next.js is currently one of the best ways for developing custom eCommerce solutions. It’s benefits include improved performance, SEO-friendliness, easy development and deployment, excellent developer experience, and the ability to handle versatile and scalable projects. By leveraging Next.js, developers can create compelling web applications that deliver an exceptional user experience while maintaining optimal performance.

-

Step-by-step video instructions for running the app

[https://www.youtube.com/watch?v=Ry0aOMws0gE](https://www.youtube.com/watch?v=Ry0aOMws0gE) @@ -137,7 +135,6 @@ DATABASE_URL="mysql://username:password@localhost:3306/singitronic_nextjs"

8. Now you need to open your terminal of choice in the root folder of the project and write:

- ``` npm install ``` @@ -177,7 +174,6 @@ npm run dev

14. Open http://localhost:3000 and see it live!

-

Project screenshots

Home page

diff --git a/app/(dashboard)/admin/categories/[id]/page.tsx b/app/(dashboard)/admin/categories/[id]/page.tsx index 8cba1ba4..37858fcd 100644 --- a/app/(dashboard)/admin/categories/[id]/page.tsx +++ b/app/(dashboard)/admin/categories/[id]/page.tsx @@ -1,10 +1,10 @@ -"use client"; -import { DashboardSidebar } from "@/components"; -import { useRouter } from "next/navigation"; -import React, { useEffect, useState } from "react"; -import toast from "react-hot-toast"; -import { formatCategoryName } from "../../../../../utils/categoryFormating"; -import { convertCategoryNameToURLFriendly } from "../../../../../utils/categoryFormating"; +'use client'; +import { DashboardSidebar } from '@/components'; +import { useRouter } from 'next/navigation'; +import React, { useEffect, useState } from 'react'; +import toast from 'react-hot-toast'; +import { formatCategoryName } from '../../../../../utils/categoryFormating'; +import { convertCategoryNameToURLFriendly } from '../../../../../utils/categoryFormating'; interface DashboardSingleCategoryProps { params: { id: number }; @@ -14,34 +14,34 @@ const DashboardSingleCategory = ({ params: { id }, }: DashboardSingleCategoryProps) => { const [categoryInput, setCategoryInput] = useState<{ name: string }>({ - name: "", + name: '', }); const router = useRouter(); const deleteCategory = async () => { const requestOptions = { - method: "DELETE", + method: 'DELETE', }; // sending API request for deleting a category fetch(`http://localhost:3001/api/categories/${id}`, requestOptions) .then((response) => { if (response.status === 204) { - toast.success("Category deleted successfully"); - router.push("/admin/categories"); + toast.success('Category deleted successfully'); + router.push('/admin/categories'); } else { - throw Error("There was an error deleting a category"); + throw Error('There was an error deleting a category'); } }) .catch((error) => { - toast.error("There was an error deleting category"); + toast.error('There was an error deleting category'); }); }; const updateCategory = async () => { if (categoryInput.name.length > 0) { const requestOptions = { - method: "PUT", - headers: { "Content-Type": "application/json" }, + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: convertCategoryNameToURLFriendly(categoryInput.name), }), @@ -52,15 +52,15 @@ const DashboardSingleCategory = ({ if (response.status === 200) { return response.json(); } else { - throw Error("Error updating a category"); + throw Error('Error updating a category'); } }) - .then((data) => toast.success("Category successfully updated")) + .then((data) => toast.success('Category successfully updated')) .catch((error) => { - toast.error("There was an error while updating a category"); + toast.error('There was an error while updating a category'); }); } else { - toast.error("For updating a category you must enter all values"); + toast.error('For updating a category you must enter all values'); return; } }; diff --git a/app/(dashboard)/admin/categories/new/page.tsx b/app/(dashboard)/admin/categories/new/page.tsx index 7bd39cbd..5f4bf420 100644 --- a/app/(dashboard)/admin/categories/new/page.tsx +++ b/app/(dashboard)/admin/categories/new/page.tsx @@ -1,19 +1,19 @@ -"use client"; -import { DashboardSidebar } from "@/components"; -import React, { useState } from "react"; -import toast from "react-hot-toast"; -import { convertCategoryNameToURLFriendly } from "../../../../../utils/categoryFormating"; +'use client'; +import { DashboardSidebar } from '@/components'; +import React, { useState } from 'react'; +import toast from 'react-hot-toast'; +import { convertCategoryNameToURLFriendly } from '../../../../../utils/categoryFormating'; const DashboardNewCategoryPage = () => { const [categoryInput, setCategoryInput] = useState({ - name: "", + name: '', }); const addNewCategory = () => { if (categoryInput.name.length > 0) { const requestOptions = { - method: "post", - headers: { "Content-Type": "application/json" }, + method: 'post', + headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: convertCategoryNameToURLFriendly(categoryInput.name), }), @@ -24,20 +24,20 @@ const DashboardNewCategoryPage = () => { if (response.status === 201) { return response.json(); } else { - throw Error("There was an error while creating category"); + throw Error('There was an error while creating category'); } }) .then((data) => { - toast.success("Category added successfully"); + toast.success('Category added successfully'); setCategoryInput({ - name: "", + name: '', }); }) .catch((error) => { - toast.error("There was an error while creating category"); + toast.error('There was an error while creating category'); }); } else { - toast.error("You need to enter values to add a category"); + toast.error('You need to enter values to add a category'); } }; return ( diff --git a/app/(dashboard)/admin/categories/page.tsx b/app/(dashboard)/admin/categories/page.tsx index 4e4301c8..4a33edf6 100644 --- a/app/(dashboard)/admin/categories/page.tsx +++ b/app/(dashboard)/admin/categories/page.tsx @@ -1,16 +1,16 @@ -"use client"; -import { CustomButton, DashboardSidebar } from "@/components"; -import { nanoid } from "nanoid"; -import Link from "next/link"; -import React, { useEffect, useState } from "react"; -import { formatCategoryName } from "../../../../utils/categoryFormating"; +'use client'; +import { CustomButton, DashboardSidebar } from '@/components'; +import { nanoid } from 'nanoid'; +import Link from 'next/link'; +import React, { useEffect, useState } from 'react'; +import { formatCategoryName } from '../../../../utils/categoryFormating'; const DashboardCategory = () => { const [categories, setCategories] = useState([]); // getting all categories to be displayed on the all categories page useEffect(() => { - fetch("http://localhost:3001/api/categories") + fetch('http://localhost:3001/api/categories') .then((res) => { return res.json(); }) diff --git a/app/(dashboard)/admin/orders/[id]/page.tsx b/app/(dashboard)/admin/orders/[id]/page.tsx index 4ee9b705..0c16a64c 100644 --- a/app/(dashboard)/admin/orders/[id]/page.tsx +++ b/app/(dashboard)/admin/orders/[id]/page.tsx @@ -1,11 +1,11 @@ -"use client"; -import { DashboardSidebar } from "@/components"; -import { isValidEmailAddressFormat, isValidNameOrLastname } from "@/lib/utils"; -import Image from "next/image"; -import Link from "next/link"; -import { useParams, useRouter } from "next/navigation"; -import React, { useEffect, useState } from "react"; -import toast from "react-hot-toast"; +'use client'; +import { DashboardSidebar } from '@/components'; +import { isValidEmailAddressFormat, isValidNameOrLastname } from '@/lib/utils'; +import Image from 'next/image'; +import Link from 'next/link'; +import { useParams, useRouter } from 'next/navigation'; +import React, { useEffect, useState } from 'react'; +import toast from 'react-hot-toast'; interface OrderProduct { id: string; @@ -29,20 +29,20 @@ interface OrderProduct { const AdminSingleOrder = () => { const [orderProducts, setOrderProducts] = useState(); const [order, setOrder] = useState({ - id: "", - adress: "", - apartment: "", - company: "", - dateTime: "", - email: "", - lastname: "", - name: "", - phone: "", - postalCode: "", - city: "", - country: "", - orderNotice: "", - status: "processing", + id: '', + adress: '', + apartment: '', + company: '', + dateTime: '', + email: '', + lastname: '', + name: '', + phone: '', + postalCode: '', + city: '', + country: '', + orderNotice: '', + status: 'processing', total: 0, }); const params = useParams<{ id: string }>(); @@ -52,7 +52,7 @@ const AdminSingleOrder = () => { useEffect(() => { const fetchOrderData = async () => { const response = await fetch( - `http://localhost:3001/api/orders/${params?.id}` + `http://localhost:3001/api/orders/${params?.id}`, ); const data: Order = await response.json(); setOrder(data); @@ -60,7 +60,7 @@ const AdminSingleOrder = () => { const fetchOrderProducts = async () => { const response = await fetch( - `http://localhost:3001/api/order-product/${params?.id}` + `http://localhost:3001/api/order-product/${params?.id}`, ); const data: OrderProduct[] = await response.json(); setOrderProducts(data); @@ -84,57 +84,57 @@ const AdminSingleOrder = () => { order?.postalCode.length > 0 ) { if (!isValidNameOrLastname(order?.name)) { - toast.error("You entered invalid name format"); + toast.error('You entered invalid name format'); return; } if (!isValidNameOrLastname(order?.lastname)) { - toast.error("You entered invalid lastname format"); + toast.error('You entered invalid lastname format'); return; } if (!isValidEmailAddressFormat(order?.email)) { - toast.error("You entered invalid email format"); + toast.error('You entered invalid email format'); return; } fetch(`http://localhost:3001/api/orders/${order?.id}`, { - method: "PUT", // or 'PUT' + method: 'PUT', // or 'PUT' headers: { - "Content-Type": "application/json", + 'Content-Type': 'application/json', }, body: JSON.stringify(order), }) .then((response) => { if (response.status === 200) { - toast.success("Order updated successfuly"); + toast.success('Order updated successfuly'); } else { - throw Error("There was an error while updating a order"); + throw Error('There was an error while updating a order'); } }) .catch((error) => - toast.error("There was an error while updating a order") + toast.error('There was an error while updating a order'), ); } else { - toast.error("Please fill all fields"); + toast.error('Please fill all fields'); } }; const deleteOrder = async () => { const requestOptions = { - method: "DELETE", + method: 'DELETE', }; fetch( `http://localhost:3001/api/order-product/${order?.id}`, - requestOptions + requestOptions, ).then((response) => { fetch( `http://localhost:3001/api/orders/${order?.id}`, - requestOptions + requestOptions, ).then((response) => { - toast.success("Order deleted successfully"); - router.push("/admin/orders"); + toast.success('Order deleted successfully'); + router.push('/admin/orders'); }); }); }; @@ -317,9 +317,9 @@ const AdminSingleOrder = () => { setOrder({ ...order, status: e.target.value as - | "processing" - | "delivered" - | "canceled", + | 'processing' + | 'delivered' + | 'canceled', }) } > @@ -336,7 +336,7 @@ const AdminSingleOrder = () => {