Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
e5ba0ca
feat: 🎸 create table component and test in order
kenmaqqe May 26, 2025
690b537
feat: 🎸 add sort component
kenmaqqe May 26, 2025
fea857d
feat: 🎸 add customInput component
kenmaqqe May 27, 2025
97d4836
refactor: 💡 add yup validation and refactore some code
kenmaqqe May 27, 2025
f50d330
refactor: 💡 remake custom table using table component from antd
kenmaqqe Jun 2, 2025
de03b9a
fix: 🐛 add actions button to table
kenmaqqe Jun 2, 2025
1d97645
feat: 🎸 create custom modal component
kenmaqqe Jun 2, 2025
2d40488
fix: 🐛 refactore and resolve all problems
kenmaqqe Jun 2, 2025
2d5bc07
fix: 🐛 small refactore
kenmaqqe Jun 2, 2025
b834c67
small fix and refactore
kenmaqqe Jun 10, 2025
5c7ba78
feat: 🎸 add onClick to actions button and test
kenmaqqe Jun 10, 2025
0a109c4
refactor: 💡 create modal component and remake modals open logic
kenmaqqe Jun 16, 2025
b05f141
feat: 🎸 create new modal architecture using entity and modalTyp
kenmaqqe Jun 16, 2025
b90e31d
fix: 🐛 resolve all changes
kenmaqqe Jun 23, 2025
b0892f2
feat: 🎸 new archirtecture for modal component
kenmaqqe Jun 23, 2025
50b38d0
fix: 🐛 rebase dataSource from orderPage to OrderTableData file
kenmaqqe Jun 23, 2025
65924c9
fix: 🐛 setting up pagination for Table component
kenmaqqe Jun 24, 2025
f1b86e1
feat: 🎸 create EditOrder and Delete modal and testing with mock
kenmaqqe Jun 24, 2025
6a00cf9
fix: 🐛 remake modal architecture and resolve changes
kenmaqqe Jun 30, 2025
d55d64b
fix: 🐛 resolve changes
kenmaqqe Jul 1, 2025
6e2077c
feat: 🎸 add logic to modal
kenmaqqe Jul 1, 2025
0d1e6b5
fix: 🐛 go use $danger varialbe for danger button styles
kenmaqqe Jul 21, 2025
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,6 @@ vitest.config.*.timestamp*

.nx
.vscode

.cursor/rules/nx-rules.mdc
.github/instructions/nx.instructions.md
1 change: 0 additions & 1 deletion apps/be-api/src/app/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import * as path from 'path';
export interface AppOptions {}

export async function app(fastify: FastifyInstance, opts: AppOptions) {

fastify.register(AutoLoad, {
dir: path.join(__dirname, 'plugins'),
options: { ...opts },
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.ActionButtons {
display: flex;
gap: 10px;
}
24 changes: 24 additions & 0 deletions apps/fe-admin/src/components/ActionButtons/ActionButtons.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React from 'react';
import { MdDelete, MdEdit } from 'react-icons/md';
import { CustomButton } from '@shopery/ui-shared';
import styles from './ActionButtons.module.scss';

type ActionButtonsProps = {
onEdit?: () => void;
onDelete?: () => void;
};

const ActionButtons = ({ onEdit, onDelete }: ActionButtonsProps) => {
return (
<div className={styles.ActionButtons}>
<CustomButton onClick={onEdit}>
<MdEdit />
</CustomButton>
<CustomButton onClick={onDelete}>
<MdDelete />
</CustomButton>
</div>
);
};

export default ActionButtons;
6 changes: 3 additions & 3 deletions apps/fe-admin/src/components/User/User.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { Button } from '@shopery/ui-shared';
import { CustomButton } from '@shopery/ui-shared';
import styles from './User.module.scss';
import { User } from '../../types';
import UserAvatarIcon from '@assets/icons/userAvatar.svg?react';
Expand All @@ -15,9 +15,9 @@ const UserComponent: React.FC<UserProps> = ({ user }) => {
<p className={styles.userName}>{user.name}</p>
<UserAvatarIcon className={styles.userIcon} />
</div>
<Button size='small' variant='fill'>
<CustomButton size='small' variant='fill'>
{'<- Log Out'}
</Button>
</CustomButton>
</div>
);
};
Expand Down
1 change: 1 addition & 0 deletions apps/fe-admin/src/components/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { default as Sidebar } from './Sidebar/Sidebar';
export { default as ActionsButtons } from './ActionButtons/ActionButtons';
86 changes: 86 additions & 0 deletions apps/fe-admin/src/data/OrderTableData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
export const dataSource = [
{
id: 1,
userId: 101,
totalNumberOfProducts: 2,
totalPrice: 50,
status: 'pending',
},
{
id: 2,
userId: 102,
totalNumberOfProducts: 3,
totalPrice: 75,
status: 'shipped',
},
{
id: 3,
userId: 103,
totalNumberOfProducts: 1,
totalPrice: 25,
status: 'delivered',
},
{
id: 4,
userId: 104,
totalNumberOfProducts: 5,
totalPrice: 100,
status: 'pending',
},
{
id: 5,
userId: 105,
totalNumberOfProducts: 4,
totalPrice: 80,
status: 'shipped',
},
{
id: 6,
userId: 106,
totalNumberOfProducts: 2,
totalPrice: 60,
status: 'delivered',
},
{
id: 7,
userId: 107,
totalNumberOfProducts: 3,
totalPrice: 90,
status: 'pending',
},
{
id: 8,
userId: 108,
totalNumberOfProducts: 1,
totalPrice: 30,
status: 'shipped',
},
{
id: 9,
userId: 109,
totalNumberOfProducts: 6,
totalPrice: 120,
status: 'delivered',
},
{
id: 10,
userId: 110,
totalNumberOfProducts: 2,
totalPrice: 40,
status: 'pending',
},
{
id: 11,
userId: 111,
totalNumberOfProducts: 3,
totalPrice: 70,
status: 'shipped',
},
{
id: 12,
userId: 112,
totalNumberOfProducts: 4,
totalPrice: 90,
status: 'delivered',
},
];
1 change: 1 addition & 0 deletions apps/fe-admin/src/data/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { dataSource } from './OrderTableData';
2 changes: 1 addition & 1 deletion apps/fe-admin/src/layouts/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export { default as PageLayout } from './pageLayout/PageLayout';
export { default as PageLayout } from './pageLayout/pageLayout';
6 changes: 3 additions & 3 deletions apps/fe-admin/src/pages/DashboardPage.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { Button } from '@shopery/ui-shared';
import { CustomButton } from '@shopery/ui-shared';

const DashboardPage = () => {
return (
<section>
<Button variant='fill' size='small'>
<CustomButton variant='fill' size='small'>
Test
</Button>
</CustomButton>
</section>
);
};
Expand Down
7 changes: 0 additions & 7 deletions apps/fe-admin/src/pages/OrderPage.tsx

This file was deleted.

Empty file.
163 changes: 163 additions & 0 deletions apps/fe-admin/src/pages/OrderPage/OrderPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
import {
CustomButton,
CustomInput,
CustomModal,
CustomSelect,
CustomTable,
} from '@shopery/ui-shared';
import { useDeferredValue, useState } from 'react';
import { Controller, useForm, useWatch } from 'react-hook-form';
import ActionButtons from '../../components/ActionButtons/ActionButtons';
import { dataSource } from '../../data';
import styles from './OrderPage.module.scss';

const OrderPage = () => {
const [orderData, setOrderData] = useState(dataSource);
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
const [orderId, setOrderId] = useState<number | null>(null);
const [editStatus, setEditStatus] = useState<string | null>(null);
const { control } = useForm({
defaultValues: {
search: '',
},
});

const columns = [
{ title: 'Order ID', dataIndex: 'id', key: 'id' },
{ title: 'User', dataIndex: 'userId', key: 'userId' },
{
title: 'Total number of products',
dataIndex: 'totalNumberOfProducts',
key: 'totalNumberOfProducts',
},
{ title: 'Total Price', dataIndex: 'totalPrice', key: 'totalPrice' },
{ title: 'Status', dataIndex: 'status', key: 'status' },
{
title: 'Actions',
key: 'actions',
render: (record) => (
<ActionButtons
onEdit={() => handleOpenEditModal(record.id, record.status)}
onDelete={() => handleOpenDeleteModal(record.id)}
/>
),
},
];

const statusOptions = [
{ value: 'pending', label: 'Pending' },
{ value: 'shipped', label: 'Shipped' },
{ value: 'delivered', label: 'Delivered' },
];

const handleOpenEditModal = (id: number, status: string) => {
setOrderId(id);
setIsEditModalOpen(true);
setEditStatus(status);
};

const handleOpenDeleteModal = (id: number) => {
setOrderId(id);
setIsDeleteModalOpen(true);
};

const searchValue = useWatch({ control, name: 'search' });
const debouncedSearch = useDeferredValue(searchValue?.trim() ?? '', 400);

const filteredData = orderData.filter((item) => {
if (!debouncedSearch) return true;
const searchStr = debouncedSearch.toLowerCase();

const matchesSearch = [
item.id,
item.userId,
item.totalNumberOfProducts,
item.totalPrice,
item.status?.toLowerCase(),
].some((field) => String(field).includes(searchStr));

return matchesSearch;
});

const handleDelete = (id: number) => {
setOrderData(orderData.filter((item) => item.id !== id));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use prev value

setIsDeleteModalOpen(false);
setOrderId(null);
};

const handleSave = () => {
if (typeof editStatus !== 'string') return;
setOrderData((prev) =>
prev.map((item) => {
if (item.id === orderId) {
return { ...item, status: editStatus };
}
return item;
})
);
setIsEditModalOpen(false);
setEditStatus(null);
setOrderId(null);
};

return (
<section className={styles.orderPage}>
<h1>Orders</h1>
<form>
<Controller
name='search'
control={control}
render={({ field, fieldState }) => (
<CustomInput
placeholder='Search...'
{...field}
hasError={!!fieldState.error}
error={fieldState.error?.message}
/>
)}
/>
</form>
<CustomSelect
prefix='Filter by status:'
options={[{ value: 'all', label: 'All' }, ...statusOptions]}
/>
<CustomTable columns={columns} dataSource={filteredData} />
<CustomModal
open={isEditModalOpen}
onCancel={() => setIsEditModalOpen(false)}
title='Edit Order'
footer={
<CustomButton variant='fill' onClick={() => handleSave()}>
Save
</CustomButton>
}
>
<CustomSelect
value={editStatus}
options={statusOptions}
onChange={(value: string) => setEditStatus(value)}
/>
</CustomModal>
<CustomModal
open={isDeleteModalOpen}
onCancel={() => setIsDeleteModalOpen(false)}
title='Delete Order'
footer={
<CustomButton
variant='danger'
onClick={() => {
handleDelete(orderId as number);
}}
>
Delete
</CustomButton>
}
>
<p>Are you sure you want to delete this order?</p>
</CustomModal>
</section>
);
};

export default OrderPage;
2 changes: 1 addition & 1 deletion apps/fe-admin/src/pages/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export { default as DashboardPage } from './DashboardPage';
export { default as OrderPage } from './OrderPage';
export { default as OrderPage } from './OrderPage/OrderPage';
export { default as ProductPage } from './ProductPage';
export { default as UserPage } from './UserPage';
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@
color: $tertiary;
}
}
.danger {
background-color: $danger;
border: none;
color: $white;
&:hover {
filter: brightness(0.9);
}
}
.small {
padding: 10px 24px;
font-size: $font-size-xs;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { clsx } from 'clsx';
import React, { ButtonHTMLAttributes } from 'react';
import styles from './Button.module.scss';
import styles from './CustomButton.module.scss';

export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
children: string;
variant?: 'fill' | 'border' | 'ghost';
children: React.ReactNode;
variant?: 'fill' | 'border' | 'ghost' | 'danger';
size?: 'small' | 'medium' | 'large';
}

export const Button: React.FC<ButtonProps> = ({
export const CustomButton: React.FC<ButtonProps> = ({
children,
variant = 'fill',
size = 'small',
Expand All @@ -30,4 +30,4 @@ export const Button: React.FC<ButtonProps> = ({
);
};

export default Button;
export default CustomButton;
Loading