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
5 changes: 5 additions & 0 deletions next-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information.
27 changes: 27 additions & 0 deletions next.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
trailingSlash: true,
output: "export",
experimental: {
esmExternals: false,
},
env: {
API_URL: process.env.API_URL,
},
images: {
unoptimized: true,
},
transpilePackages: [
"antd",
"@ant-design",
"rc-util",
"rc-pagination",
"rc-picker",
"rc-notification",
"rc-tooltip",
"rc-tree",
"rc-table",
],
};

export default nextConfig;
31 changes: 31 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"name": "charlie-ecommerce-shopping-portal",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"antd": "^5.21.5",
"autoprefixer": "^10.4.20",
"axios": "^1.7.7",
"dotenv": "^16.4.5",
"next": "^14.2.16",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-loader-spinner": "^6.1.6",
"react-spring-bottom-sheet": "^3.4.1",
"react-toastify": "^10.0.6",
"sass": "^1.80.3",
"tailwindcss": "^3.4.14",
"typescript": "^5.6.3",
"zustand": "^5.0.0"
},
"devDependencies": {
"@types/node": "22.7.8",
"@types/react": "18.3.11"
}
}
6 changes: 6 additions & 0 deletions postcss.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};
Binary file added public/assets/cart.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/delete.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/dessert.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/empty.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/minus.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/plus.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/success.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/tree.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions renovate.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended"
]
}
48 changes: 48 additions & 0 deletions src/components/button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
"use client";
import { LoadingIcon } from "./loading-icon";

interface ICustomButton {
buttonText: string;
customStyle?: string;
onClickFunc?: Function;
disabled?: boolean;
fullWidth?: boolean;
isLoading?: boolean;
}

export const CustomButton = (props: ICustomButton) => {
const {
buttonText = "",
customStyle = "",
onClickFunc,
disabled = false,
fullWidth = true,
isLoading = false,
} = props;

const action = () => {
if (onClickFunc) return onClickFunc();
return null;
};

const defaultStyle = `border-0 bg-primary text-xs tracking-wider ${
fullWidth && "w-full"
}`;

return (
<button
onClick={() => action()}
disabled={disabled}
className={
customStyle
? `${customStyle} ${defaultStyle} ${
disabled && "cursor-not-allowed"
} `
: `${defaultStyle}`
}
>
{isLoading && <LoadingIcon size={35} />}
{!isLoading && buttonText}
</button>
);
};
39 changes: 39 additions & 0 deletions src/components/cart/cart-item.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
"use client";
import React from "react";
import ImageContainer from "../image";
import { createCartStore, ICart } from "@/stores/cart";

interface ICartItem {
cart: ICart;
showSmallAmount?: boolean;
showDeleteIcon?: boolean;
}

export default function CartItem(props: ICartItem) {
const { cart, showSmallAmount = true, showDeleteIcon } = props;
const { decrementQty } = createCartStore();

return (
<div className="flex items-center border border-t-transparent border-l-transparent border-r-transparent">
<div className="flex-1">
<p className="label !text-[#716A67]">{cart.name}</p>

{/* Quantity & Total */}
<div className="flex pt-s py-s justify-start items-start">
<p className="txt text-[#bc7964]">{cart.qty > 0 && cart.qty}</p>
{showSmallAmount && <p className="label ml-s">@{cart.price}</p>}
<p className="label ml-s">${cart.price * cart.qty}</p>
</div>
</div>
{!showSmallAmount && <p className="title">${cart.price}</p>}
{showDeleteIcon && (
<ImageContainer
className="cursor-pointer"
src="/assets/delete.png"
alt="delete-icon"
onClickFunc={() => decrementQty(cart.id)}
/>
)}
</div>
);
}
53 changes: 53 additions & 0 deletions src/components/cart/carts.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
"use client";
import React from "react";
import CartItem from "./cart-item";
import { createCartStore } from "@/stores/cart";
import ImageContainer from "../image";

interface ICart {
showSmallAmount?: boolean;
showDeleteIcon?: boolean;
}

export default function Carts(props: ICart) {
const { showSmallAmount = false, showDeleteIcon = true } = props;
const { carts, getTotalAmount } = createCartStore();

return (
<>
{/* Empty Cart */}
{Object.keys(carts).length === 0 && (
<div className="mt-l flex flex-col items-center">
<ImageContainer
src="/assets/empty.png"
height={100}
width={120}
alt="empty-cart-icon"
/>
<p className="pt-l text-xs text-[#988A87]">
Your added items will appear here
</p>
</div>
)}
<div className="mt-s overflow-y-auto max-h-[300px] scrollbar-hide">
{Object.keys(carts).length > 0 &&
Object.values(carts).map((cart, key) => (
<div key={key}>
<CartItem
cart={cart}
showSmallAmount={showSmallAmount}
showDeleteIcon={showDeleteIcon}
/>
</div>
))}
</div>
{/* Order Total */}
{Object.keys(carts).length > 0 && (
<div className="flex justify-between items-center mt-l">
<p className="label txt-[#928D8B]">Order Total</p>
<p className="total">${getTotalAmount()}</p>
</div>
)}
</>
);
}
10 changes: 10 additions & 0 deletions src/components/container.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
"use client";
import React from "react";

interface IContainer {
children: React.ReactNode;
}

export const Container = ({ children }: IContainer) => {
return <div className="mx-auto min-h-screen">{children}</div>;
};
37 changes: 37 additions & 0 deletions src/components/image.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"use client";
import React from "react";
import Image from "next/image";

interface ImageContainer {
className?: string;
src: string;
width?: number;
height?: number;
alt: string;
onClickFunc?: () => void;
}

export default function ImageContainer(props: ImageContainer) {
const {
className = "object-contain",
src = "",
width = 15,
height = 15,
alt = "",
onClickFunc,
} = props;

return (
<div className="overflow-hidden" onClick={onClickFunc}>
<Image
className={className}
src={src}
priority
width={width}
height={height}
alt={alt}
quality={100}
/>
</div>
);
}
24 changes: 24 additions & 0 deletions src/components/loading-icon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
"use client";
import React from "react";
import { Rings } from "react-loader-spinner";

interface ILoadingIcon {
color: string;
size: string;
}

export const LoadingIcon = (props: ILoadingIcon) => {
const { color = "#C73B0E", size = "50" } = props;
return (
<Rings
height={size}
width={size}
color={color}
radius="6"
wrapperStyle={{}}
wrapperClass="loading-icon"
visible={true}
ariaLabel="rings-loading"
/>
);
};
45 changes: 45 additions & 0 deletions src/components/modal/bottom-modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
"use client";
import React from "react";
import { BottomSheet } from "react-spring-bottom-sheet";
import "react-spring-bottom-sheet/dist/style.css";

interface IBottomModal {
open: boolean;
setOpen: (value: boolean) => void;
children: React.ReactNode;
onCloseAction?: () => void;
spaceDiscard?: boolean;
}

export const BottomModal = (props: IBottomModal) => {
const {
open,
setOpen,
children,
onCloseAction = null,
spaceDiscard = false,
} = props;

const dismiss = () => {
onCloseAction && onCloseAction();
setOpen(false);
};

return (
<BottomSheet
open={open}
onDismiss={() => dismiss()}
style={{
pointerEvents: spaceDiscard ? "none" : "all",
}}
>
<div
style={{
pointerEvents: "all",
}}
>
{children}
</div>
</BottomSheet>
);
};
17 changes: 17 additions & 0 deletions src/components/modal/mobile-confirm-modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"use client";
import { BottomModal } from "@/components/modal/bottom-modal";
import { createCartStore } from "@/stores/cart";
import React from "react";

export default function MobileConfirmationModal() {
const { openMobileModal, setOpenMobileModal } = createCartStore();
return (
<BottomModal
setOpen={setOpenMobileModal}
open={openMobileModal}
onCloseAction={() => setOpenMobileModal(false)}
>
<h1> Carts Items</h1>
</BottomModal>
);
}
Loading