diff --git a/apps/backoffice/src/app/_hooks/use-login.ts b/apps/backoffice/src/app/_hooks/use-login.ts index e23709a..3c031d3 100644 --- a/apps/backoffice/src/app/_hooks/use-login.ts +++ b/apps/backoffice/src/app/_hooks/use-login.ts @@ -5,7 +5,7 @@ import { usePostLogin, } from '@imphnen-frontend-service/service'; import { zodResolver } from '@hookform/resolvers/zod'; -import { useNavigate } from 'react-router-dom'; +import { toast } from 'sonner'; export const useLogin = () => { const postLogin = usePostLogin(); @@ -13,14 +13,11 @@ export const useLogin = () => { resolver: zodResolver(authLoginSchema), mode: 'all', }); - const navigate = useNavigate(); const onSubmit = form.handleSubmit((data) => { postLogin.mutate(data, { - onSuccess: () => { - console.log('Success Login'); - navigate('/dashboard'); - }, + onSuccess: () => toast.success("Login sukses"), + onError: (error) => toast.error(error.message), }); }); diff --git a/apps/backoffice/src/app/dashboard/_components/modal-add-item.tsx b/apps/backoffice/src/app/dashboard/_components/modal-add-item.tsx index 520cfe4..5e0aa55 100644 --- a/apps/backoffice/src/app/dashboard/_components/modal-add-item.tsx +++ b/apps/backoffice/src/app/dashboard/_components/modal-add-item.tsx @@ -1,10 +1,12 @@ import { Button } from '@imphnen-frontend-service/ui/atoms'; -import { InputField, Modal } from '@imphnen-frontend-service/ui/molecules'; +import { Modal } from '@imphnen-frontend-service/ui/molecules'; +import { useConfirmItem, useItem } from '../_hook/use-item'; +import { ControlledInputField } from '@imphnen-frontend-service/ui/organisms'; interface IModalAddItem { isOpen: boolean; onClose: () => void; - handleAddItem: () => void; + handleAddItem?: () => Promise; currentStep?: number; nextStep: () => void; prevStep: () => void; @@ -46,91 +48,114 @@ interface IStepOneProps { onClose: () => void; } -const StepOne = ({ nextStep }: IStepOneProps) => ( - <> - -

- Tambah Item Gacha -

-

- Lengkapi detail di bawah ini untuk menambahkan item gacha -

-
- -
- - - -
+const StepOne = ({ nextStep }: IStepOneProps) => { + const { form, onSubmit } = useItem(nextStep); - -
- -); + return ( + <> + +

+ Tambah Item Gacha +

+

+ Lengkapi detail di bawah ini untuk menambahkan item gacha +

+
+ +
+
+ + + +
+ + +
+
+ + ); +}; interface IStepTwoProps { onClose: () => void; - handleAddItem?: () => void; + handleAddItem?: () => Promise; resetStep: () => void; } -const StepTwo = ({ onClose, handleAddItem, resetStep }: IStepTwoProps) => ( - <> - -

- Tambah Item -

-

- Apakah kamu yakin ingin -
menambahkan item ini? -

-
- - - - - -); +const StepTwo = ({ onClose, handleAddItem, resetStep }: IStepTwoProps) => { + const { onConfirm, onCancel } = useConfirmItem( + onClose, + resetStep, + handleAddItem, + { + success: 'Item ditambahkan ke gacha item', + error: 'Item gagal ditambahkan ke gacha item', + } + ); + + return ( + <> + +

+ Tambah Item +

+

+ Apakah kamu yakin ingin +
menambahkan item ini? +

+
+ + + + + + ); +}; export default ModalAddItem; diff --git a/apps/backoffice/src/app/dashboard/_components/modal-edit-item.tsx b/apps/backoffice/src/app/dashboard/_components/modal-edit-item.tsx index 4d9bc7e..19a22fe 100644 --- a/apps/backoffice/src/app/dashboard/_components/modal-edit-item.tsx +++ b/apps/backoffice/src/app/dashboard/_components/modal-edit-item.tsx @@ -1,10 +1,12 @@ import { Button } from '@imphnen-frontend-service/ui/atoms'; import { InputField, Modal } from '@imphnen-frontend-service/ui/molecules'; +import { useConfirmItem, useItem } from '../_hook/use-item'; +import { ControlledInputField } from '@imphnen-frontend-service/ui/organisms'; interface IModalEditItem { isOpen: boolean; onClose: () => void; - handleEditItem?: () => void; + handleEditItem?: () => Promise; currentStep?: number; nextStep: () => void; prevStep: () => void; @@ -46,91 +48,117 @@ interface IStepOneProps { onClose: () => void; } -const StepOne = ({ nextStep }: IStepOneProps) => ( - <> - -

- Edit Item Gacha -

-

- Silakan mengubah detail dari item yang diperlukan -

-
- -
- - - -
+const StepOne = ({ nextStep }: IStepOneProps) => { + const initialValues = { + itemName: 'Hoodie IMPHNEN Official 2025', + quantity: 10, + }; - -
- -); + const { form, onSubmit } = useItem(nextStep, initialValues); + + return ( + <> + +

+ Edit Item Gacha +

+

+ Silakan mengubah detail dari item yang diperlukan +

+
+ +
+
+ + + +
+ + +
+
+ + ); +}; interface IStepTwoProps { onClose: () => void; - handleEditItem?: () => void; + handleEditItem?: () => Promise; resetStep: () => void; } -const StepTwo = ({ onClose, handleEditItem, resetStep }: IStepTwoProps) => ( - <> - -

- Update Item -

-

- Apakah kamu yakin dengan -
perubahan yang dilakukan? -

-
- - - - - -); +const StepTwo = ({ onClose, handleEditItem, resetStep }: IStepTwoProps) => { + const { onConfirm, onCancel } = useConfirmItem( + onClose, + resetStep, + handleEditItem, + { + success: 'Perubahan item berhasil dilakukan', + error: 'Perubahan item gagal dilakukan', + } + ); + return ( + <> + +

+ Update Item +

+

+ Apakah kamu yakin dengan +
perubahan yang dilakukan? +

+
+ + + + + + ); +}; export default ModalEditItem; diff --git a/apps/backoffice/src/app/dashboard/_hook/use-item.ts b/apps/backoffice/src/app/dashboard/_hook/use-item.ts new file mode 100644 index 0000000..d432d56 --- /dev/null +++ b/apps/backoffice/src/app/dashboard/_hook/use-item.ts @@ -0,0 +1,61 @@ +import { useForm } from 'react-hook-form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { + gachaItemSchema, + TGachaItem +} from '@imphnen-frontend-service/service'; +import { toast } from 'sonner'; + +export const useItem = ( + nextStep: () => void, + initialValues?: TGachaItem +) => { + const form = useForm({ + resolver: zodResolver(gachaItemSchema), + mode: 'all', + defaultValues: initialValues, + }); + + const onSubmit = form.handleSubmit((data) => { + console.log('Form data:', data); + nextStep(); + }); + + return { + form, + onSubmit, + }; +}; + +export const useConfirmItem = ( + onClose: () => void, + resetStep: () => void, + actionFunction?: () => Promise, + messages?: { + success?: string; + error?: string; + } +) => { + const onConfirm = async () => { + try { + // const result = await actionFunction?.(); + // result ? toast.success(messages?.success) : toast.error(messages?.error); + toast.success(messages?.success); + onClose(); + resetStep(); + } catch (error) { + console.log(error); + toast.error(messages?.error); + } + }; + + const onCancel = () => { + onClose(); + resetStep(); + }; + + return { + onConfirm, + onCancel, + }; +}; diff --git a/apps/backoffice/src/app/dashboard/page.tsx b/apps/backoffice/src/app/dashboard/page.tsx index d337e53..be30690 100644 --- a/apps/backoffice/src/app/dashboard/page.tsx +++ b/apps/backoffice/src/app/dashboard/page.tsx @@ -31,20 +31,17 @@ export const Components: FC = (): ReactElement => { return (
- {/* Dashboard Header */}

Dashboard

- {/* Summary Section */}

Summary

- {/* Participants */}
@@ -55,7 +52,6 @@ export const Components: FC = (): ReactElement => {
- {/* Roll and Reroll */}
@@ -68,7 +64,6 @@ export const Components: FC = (): ReactElement => {
- {/* Redeem */}
@@ -79,7 +74,6 @@ export const Components: FC = (): ReactElement => {
- {/* Inactive Users */}
@@ -94,7 +88,6 @@ export const Components: FC = (): ReactElement => {
- {/* Gacha Items Section */}

@@ -111,21 +104,20 @@ export const Components: FC = (): ReactElement => {

- {/* Gacha Items List */}
{[1, 2, 3, 4, 5, 6].map((item) => (
-
+

Lanyard IMPHNEN

-
+
Prize {item} - Chance Rate: (0.1%) + Quantity: 10
@@ -148,12 +140,7 @@ export const Components: FC = (): ReactElement => {
- {/* Lebih baik gunakan gambar yang sudah di-clip dengan size height: 78px daripada hard-code object-position dan margin */} - Lanyard IMPHNEN + Lanyard IMPHNEN
))}
@@ -174,9 +161,6 @@ export const Components: FC = (): ReactElement => { currentStep={currentStep} isOpen={showModalAddItem} onClose={() => setShowModalAddItem(false)} - handleAddItem={() => { - console.log('Item added'); - }} nextStep={nextStep} prevStep={prevStep} resetStep={resetStep} @@ -187,9 +171,6 @@ export const Components: FC = (): ReactElement => { currentStep={currentStep} isOpen={showModalEditItem} onClose={() => setShowModalEditItem(false)} - handleEditItem={() => { - console.log('Item edited'); - }} nextStep={nextStep} prevStep={prevStep} resetStep={resetStep} @@ -199,9 +180,6 @@ export const Components: FC = (): ReactElement => { setShowModalDeleteItem(false)} - handleDeleteItem={() => { - console.log('Item deleted'); - }} /> ); diff --git a/apps/backoffice/src/app/gacha-roll/_components/modal-add-item.tsx b/apps/backoffice/src/app/gacha-roll/_components/modal-add-item.tsx new file mode 100644 index 0000000..46a59ea --- /dev/null +++ b/apps/backoffice/src/app/gacha-roll/_components/modal-add-item.tsx @@ -0,0 +1,160 @@ +import { Button } from '@imphnen-frontend-service/ui/atoms'; +import { Modal } from '@imphnen-frontend-service/ui/molecules'; +import { ControlledInputField } from '@imphnen-frontend-service/ui/organisms'; +import { useItem, useConfirmItem } from '../_hook/use-item'; + +interface IModalAddItem { + isOpen: boolean; + onClose: () => void; + handleAddItem?: () => Promise; + currentStep?: number; + nextStep: () => void; + prevStep: () => void; + resetStep: () => void; +} + +const ModalAddItem = ({ + isOpen, + onClose, + currentStep, + nextStep, + resetStep, + handleAddItem, +}: IModalAddItem) => { + return ( + { + onClose(); + resetStep(); + }} + disableEscapeKeyDown={true} + > + {currentStep === 1 && } + {currentStep === 2 && ( + + )} + + ); +}; + +interface IStepOneProps { + nextStep: () => void; + onClose: () => void; +} + +const StepOne = ({ nextStep }: IStepOneProps) => { + const { form, onSubmit } = useItem(nextStep); + + return ( + <> + +

+ Tambah Item Roll Gacha +

+

+ Lengkapi detal di bawah ini, untuk menambahkan item gacha +

+
+ +
+
+ + + +
+ + +
+
+ + ); +}; + +interface IStepTwoProps { + onClose: () => void; + handleAddItem?: () => Promise; + resetStep: () => void; +} + +const StepTwo = ({ onClose, handleAddItem, resetStep }: IStepTwoProps) => { + const { onConfirm, onCancel } = useConfirmItem( + onClose, + resetStep, + handleAddItem, + { + success: 'Item ditambahkan ke roll gacha', + error: 'Item gagal ditambahkan ke roll gacha', + } + ); + + return ( + <> + +

+ Tambah ke Roll Gacha +

+

+ Apakah kamu yakin ingin +
menambahkan item ini ke roll gacha? +

+
+ + + + + + ); +}; + +export default ModalAddItem; diff --git a/apps/backoffice/src/app/gacha-roll/_components/modal-delete-item.tsx b/apps/backoffice/src/app/gacha-roll/_components/modal-delete-item.tsx new file mode 100644 index 0000000..3663ee0 --- /dev/null +++ b/apps/backoffice/src/app/gacha-roll/_components/modal-delete-item.tsx @@ -0,0 +1,62 @@ +import { Button } from '@imphnen-frontend-service/ui/atoms'; +import { Modal } from '@imphnen-frontend-service/ui/molecules'; + +interface IModalDeleteItem { + isOpen: boolean; + onClose: () => void; + handleDeleteItem?: () => void; +} + +const ModalDeleteItem = ({ + isOpen, + onClose, + handleDeleteItem, +}: IModalDeleteItem) => { + return ( + + + Delete item? +
+

+ Delete Item +

+

+ Apakah kamu yakin untuk menghapus item ini? +

+
+
+ + + + +
+ ); +}; + +export default ModalDeleteItem; diff --git a/apps/backoffice/src/app/gacha-roll/_components/modal-update-item.tsx b/apps/backoffice/src/app/gacha-roll/_components/modal-update-item.tsx new file mode 100644 index 0000000..770f867 --- /dev/null +++ b/apps/backoffice/src/app/gacha-roll/_components/modal-update-item.tsx @@ -0,0 +1,168 @@ +import { Button } from '@imphnen-frontend-service/ui/atoms'; +import { Modal } from '@imphnen-frontend-service/ui/molecules'; +import { useConfirmItem, useItem } from '../_hook/use-item'; +import { ControlledInputField } from '@imphnen-frontend-service/ui/organisms'; + +interface IModalUpdateItem { + isOpen: boolean; + onClose: () => void; + handleUpdateItem?: () => Promise; + currentStep?: number; + nextStep: () => void; + prevStep: () => void; + resetStep: () => void; +} + +const ModalUpdateItem = ({ + isOpen, + onClose, + currentStep, + nextStep, + resetStep, + handleUpdateItem, +}: IModalUpdateItem) => { + return ( + { + onClose(); + resetStep(); + }} + disableEscapeKeyDown={true} + > + {currentStep === 1 && } + {currentStep === 2 && ( + + )} + + ); +}; + +interface IStepOneProps { + nextStep: () => void; + onClose: () => void; +} + +const StepOne = ({ nextStep }: IStepOneProps) => { + const initialValues = { + itemName: 'Hoodie IMPHNEN Official 2025', + quantity: 10, + chanceRate: 0.1, + }; + + const { form, onSubmit } = useItem(nextStep, initialValues); + + return ( + <> + +

+ Update Item Roll Gacha +

+
+ +
+
+ + + +
+ + +
+
+ + ); +}; + +interface IStepTwoProps { + onClose: () => void; + handleUpdateItem?: () => Promise; + resetStep: () => void; +} + +const StepTwo = ({ onClose, handleUpdateItem, resetStep }: IStepTwoProps) => { + const { onConfirm, onCancel } = useConfirmItem( + onClose, + resetStep, + handleUpdateItem, + { + success: 'Perubahan item roll berhasil dilakukan', + error: 'Perubahan item roll gagal dilakukan', + } + ); + + return ( + <> + +

+ Update Item +

+

+ Apakah kamu yakin dengan +
perubahan yang dilakukan? +

+
+ + + + + + ); +}; + +export default ModalUpdateItem; diff --git a/apps/backoffice/src/app/gacha-roll/_hook/use-item.ts b/apps/backoffice/src/app/gacha-roll/_hook/use-item.ts new file mode 100644 index 0000000..1889359 --- /dev/null +++ b/apps/backoffice/src/app/gacha-roll/_hook/use-item.ts @@ -0,0 +1,61 @@ +import { useForm } from 'react-hook-form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { + gachaRollItemSchema, + TGachaRollItem +} from '@imphnen-frontend-service/service'; +import { toast } from 'sonner'; + +export const useItem = ( + nextStep: () => void, + initialValues?: TGachaRollItem +) => { + const form = useForm({ + resolver: zodResolver(gachaRollItemSchema), + mode: 'all', + defaultValues: initialValues, + }); + + const onSubmit = form.handleSubmit((data) => { + console.log('Form data:', data); + nextStep(); + }); + + return { + form, + onSubmit, + }; +}; + +export const useConfirmItem = ( + onClose: () => void, + resetStep: () => void, + actionFunction?: () => Promise, + messages?: { + success?: string; + error?: string; + } +) => { + const onConfirm = async () => { + try { + // const result = await actionFunction?.(); + // result ? toast.success(messages?.success) : toast.error(messages?.error); + toast.success(messages?.success); + onClose(); + resetStep(); + } catch (error) { + console.log(error); + toast.error(messages?.error); + } + }; + + const onCancel = () => { + onClose(); + resetStep(); + }; + + return { + onConfirm, + onCancel, + }; +}; diff --git a/apps/backoffice/src/app/gacha-roll/layout.tsx b/apps/backoffice/src/app/gacha-roll/layout.tsx new file mode 100644 index 0000000..a770c99 --- /dev/null +++ b/apps/backoffice/src/app/gacha-roll/layout.tsx @@ -0,0 +1,18 @@ +import { FC, ReactElement } from 'react'; +import { Outlet } from 'react-router-dom'; +import { BackofficeSidebar } from '@imphnen-frontend-service/ui/organisms'; + +export const AppLayout: FC = (): ReactElement => { + return ( +
+
+ +
+ +
+
+
+ ); +}; + +export default AppLayout; diff --git a/apps/backoffice/src/app/gacha-roll/page.tsx b/apps/backoffice/src/app/gacha-roll/page.tsx new file mode 100644 index 0000000..c57d204 --- /dev/null +++ b/apps/backoffice/src/app/gacha-roll/page.tsx @@ -0,0 +1,210 @@ +import * as React from 'react'; + +import { FC, Fragment, ReactElement, useState } from 'react'; +import { + SearchOutlined, + EditOutlined, + DeleteOutlined, + PlusOutlined, +} from '@ant-design/icons'; +import { Button, Input } from '@imphnen-frontend-service/ui/atoms'; +import { DataTable } from '@imphnen-frontend-service/ui/organisms'; + +import { + ColumnDef, + getCoreRowModel, + getPaginationRowModel, + PaginationState, + useReactTable, + RowSelectionState, +} from '@tanstack/react-table'; +import ModalAddItem from './_components/modal-add-item'; +import ModalUpdateItem from './_components/modal-update-item'; +import ModalDeleteItem from './_components/modal-delete-item'; +import { useQueryState } from '@imphnen-frontend-service/utils'; + +interface GachaItem { + id: number; + name: string; + chanceRate: number; + quantity: number; +} + +const mockData: GachaItem[] = Array.from({ length: 90 }, (_, i) => ({ + id: i + 1, + name: 'Hoodie IMPHNEN Official 2025', + chanceRate: 0.1, + quantity: 10, +})); + +export const Components: FC = (): ReactElement => { + const [showModalAddItem, setShowModalAddItem] = useState(false); + const [showModalUpdateItem, setShowModalUpdateItem] = useState(false); + const [showModalDeleteItem, setShowModalDeleteItem] = useState(false); + + const { + step: currentStep, + nextStep, + prevStep, + resetStep, + } = useQueryState('step', { + defaultValue: 1, + maxValue: 2, + minValue: 1, + }); + + const [pagination, setPagination] = React.useState({ + pageIndex: 0, + pageSize: 9, + }); + + const [rowSelection, setRowSelection] = React.useState({}); + + const columns: ColumnDef[] = [ + { + id: 'select', + header: ({ table }) => ( + + ), + cell: ({ row }) => ( + + ), + }, + { + header: 'No', + accessorKey: 'id', + }, + { + header: 'Nama Item', + accessorKey: 'name', + }, + { + header: 'Chance Rate', + accessorKey: 'chanceRate', + }, + { + header: 'Quantity', + accessorKey: 'quantity', + }, + { + header: 'Action', + cell: () => ( +
+ + +
+ ), + }, + ]; + + const table = useReactTable({ + data: mockData, + columns, + state: { + pagination, + rowSelection, + }, + enableRowSelection: true, + onRowSelectionChange: setRowSelection, + getCoreRowModel: getCoreRowModel(), + getPaginationRowModel: getPaginationRowModel(), + onPaginationChange: setPagination, + pageCount: Math.ceil(mockData.length / pagination.pageSize), + manualPagination: false, + }); + + return ( + +
+
+

Gacha Roll

+
+ +
+
+
+ +
+ +
+
+
+ +
+
+ + +
+
+ + setShowModalAddItem(false)} + nextStep={nextStep} + prevStep={prevStep} + resetStep={resetStep} + /> + setShowModalUpdateItem(false)} + nextStep={nextStep} + prevStep={prevStep} + resetStep={resetStep} + /> + setShowModalDeleteItem(false)} + handleDeleteItem={() => { + console.log('Item deleted'); + }} + /> +
+ ); +}; + +export default Components; diff --git a/apps/backoffice/src/main.tsx b/apps/backoffice/src/main.tsx index 7201952..4f9c907 100644 --- a/apps/backoffice/src/main.tsx +++ b/apps/backoffice/src/main.tsx @@ -8,6 +8,7 @@ import { convertPagesToRoute, QueryProvider, } from '@imphnen-frontend-service/utils'; +import { Toaster } from 'sonner'; import './index.css'; const files = import.meta.glob('./app/**/*(page|layout).tsx'); @@ -34,6 +35,7 @@ if (!rootElement) throw new Error('Failed to find the root element'); createRoot(rootElement).render( + diff --git a/libs/service/src/schemas/gacha/index.ts b/libs/service/src/schemas/gacha/index.ts new file mode 100644 index 0000000..765037d --- /dev/null +++ b/libs/service/src/schemas/gacha/index.ts @@ -0,0 +1,49 @@ +import { z } from 'zod'; + +export const gachaItemSchema = z.object({ + itemName: z + .string({ + required_error: 'Nama item tidak boleh kosong', + invalid_type_error: 'Nama item harus berupa string', + }) + .min(1, 'Nama item tidak boleh kosong'), + quantity: z + .number({ + required_error: 'Quantity tidak boleh kosong', + invalid_type_error: 'Quantity harus berupa angka', + }) + .min(1, 'Quantity paling sedikit adalah 1'), + foto: z + .instanceof(File) + .optional() + .refine( + (file) => !file || file.size <= 5000000, // 5MB in bytes + 'Ukuran file maksimal 5MB' + ) + .refine( + (file) => !file || ['image/jpeg', 'image/png', 'image/webp'].includes(file.type), + 'Format file harus JPG, PNG, atau WEBP' + ) +}); + +export const gachaRollItemSchema = z.object({ + itemName: z + .string({ + required_error: 'Nama item tidak boleh kosong', + invalid_type_error: 'Nama item harus berupa string', + }) + .min(1, 'Nama item tidak boleh kosong'), + quantity: z + .number({ + required_error: 'Quantity tidak boleh kosong', + invalid_type_error: 'Quantity harus berupa angka', + }) + .min(1, 'Quantity paling sedikit adalah 1'), + chanceRate: z + .number({ + required_error: 'Chance rate tidak boleh kosong', + invalid_type_error: 'Chance rate harus berupa angka', + }) + .min(0.1, 'Chance rate paling sedikit adalah 0,1') + .max(1, 'Chance rate paling banyak adalah 1'), +}); diff --git a/libs/service/src/schemas/index.ts b/libs/service/src/schemas/index.ts index 269586e..94e94e5 100644 --- a/libs/service/src/schemas/index.ts +++ b/libs/service/src/schemas/index.ts @@ -1 +1,2 @@ export * from './auth'; +export * from './gacha'; diff --git a/libs/service/src/types/gacha/index.ts b/libs/service/src/types/gacha/index.ts index cb0ff5c..14a1415 100644 --- a/libs/service/src/types/gacha/index.ts +++ b/libs/service/src/types/gacha/index.ts @@ -1 +1,11 @@ -export {}; +export type TGachaItem = { + itemName: string; + quantity: number; + foto?: File; +}; + +export type TGachaRollItem = { + itemName: string; + quantity: number; + chanceRate: number; +}; diff --git a/libs/ui/src/atoms/button/button.spec.tsx b/libs/ui/src/atoms/button/button.spec.tsx index d6c2dd3..bb74faa 100644 --- a/libs/ui/src/atoms/button/button.spec.tsx +++ b/libs/ui/src/atoms/button/button.spec.tsx @@ -24,9 +24,9 @@ describe('Test Button Component', () => { const button = screen.getByText('Delete'); - expect(button).toHaveClass('bg-danger-500'); - expect(button).toHaveClass('hover:bg-danger-600'); - expect(button).toHaveClass('text-white'); + expect(button).toHaveClass('bg-danger-100'); + expect(button).toHaveClass('hover:bg-danger-200'); + expect(button).toHaveClass('text-danger-500'); }); it("disables the button when 'disabled' prop is set", async () => { diff --git a/libs/ui/src/atoms/button/button.tsx b/libs/ui/src/atoms/button/button.tsx index dc831b5..20c1677 100644 --- a/libs/ui/src/atoms/button/button.tsx +++ b/libs/ui/src/atoms/button/button.tsx @@ -31,7 +31,7 @@ const variantClasses: Record = { bordered: 'border border-primary-500 hover:border-primary-600 bg-transparent hover:text-primary-600 hover:bg-gray-50 text-primary-500', success: 'bg-success-500 hover:bg-success-600 text-white shadow-md', - danger: 'bg-danger-500 hover:bg-danger-600 text-white shadow-md', + danger: 'bg-danger-100 hover:bg-danger-200 text-danger-500 shadow-md', }; const sizeClasses: Record = { diff --git a/libs/ui/src/atoms/input/input.tsx b/libs/ui/src/atoms/input/input.tsx index e780059..d8a6bf3 100644 --- a/libs/ui/src/atoms/input/input.tsx +++ b/libs/ui/src/atoms/input/input.tsx @@ -9,7 +9,7 @@ import { EyeInvisibleOutlined, EyeOutlined } from '@ant-design/icons'; // Import import { cn } from '@imphnen-frontend-service/utils'; import { Button } from '../button'; -type TInputType = 'text' | 'email' | 'password' | 'file'; +type TInputType = 'text' | 'email' | 'number' | 'password' | 'file'; type TInputSize = 'sm' | 'md' | 'lg'; type TInputProps = Omit< diff --git a/libs/ui/src/molecules/input-field/input-field.spec.tsx b/libs/ui/src/molecules/input-field/input-field.spec.tsx index 948c77b..909b56b 100644 --- a/libs/ui/src/molecules/input-field/input-field.spec.tsx +++ b/libs/ui/src/molecules/input-field/input-field.spec.tsx @@ -3,7 +3,7 @@ import { InputField } from './input-field'; describe('InputField Component', () => { it('renders correctly with disabled prop', () => { - render(); + render(); const input = screen.getByLabelText('Test Label'); expect(input).toBeDisabled(); @@ -11,7 +11,7 @@ describe('InputField Component', () => { }); it('renders correctly without disabled prop', () => { - render(); + render(); const input = screen.getByLabelText('Test Label'); expect(input).not.toBeDisabled(); diff --git a/libs/ui/src/molecules/input-field/input-field.tsx b/libs/ui/src/molecules/input-field/input-field.tsx index d7b4901..463be6c 100644 --- a/libs/ui/src/molecules/input-field/input-field.tsx +++ b/libs/ui/src/molecules/input-field/input-field.tsx @@ -7,7 +7,7 @@ import { import { Input } from '../../atoms'; import { cn } from '@imphnen-frontend-service/utils'; -export type TInputType = 'text' | 'email' | 'password' | 'file'; +export type TInputType = 'text' | 'email' | 'number' | 'password' | 'file'; export type TInputSize = 'sm' | 'md' | 'lg'; export type TInputFieldProps = Omit< DetailedHTMLProps, HTMLInputElement>, diff --git a/libs/ui/src/organisms/backoffice-sidebar/backoffice-sidebar.tsx b/libs/ui/src/organisms/backoffice-sidebar/backoffice-sidebar.tsx index 3ceb18e..7239b64 100644 --- a/libs/ui/src/organisms/backoffice-sidebar/backoffice-sidebar.tsx +++ b/libs/ui/src/organisms/backoffice-sidebar/backoffice-sidebar.tsx @@ -3,6 +3,7 @@ import { AuditOutlined, InboxOutlined, LogoutOutlined, + ReloadOutlined, UserOutlined, } from '@ant-design/icons'; import { Button } from '../../atoms'; @@ -33,6 +34,18 @@ export const BackofficeSidebar: FC = (): ReactElement => { Dashboard & Set Gacha + + + Gacha Roll + + ( props: TControlledInputFieldProps ) => { const { field, fieldState } = useController(props); + + const handleChange = (e: React.ChangeEvent) => { + let value; + + if (props.type === 'number') { + value = Number(e.target.value); + } else if (props.type === 'file') { + value = e.target.files?.[0]; + } else { + value = e.target.value; + } + field.onChange(value); + }; + + const inputProps = + props.type === 'file' + ? { ...props, ...field, value: undefined } + : { ...props, ...field }; + return ( - + ); }; diff --git a/libs/ui/src/organisms/datatable/datatable.tsx b/libs/ui/src/organisms/datatable/datatable.tsx index 0a52130..9f28916 100644 --- a/libs/ui/src/organisms/datatable/datatable.tsx +++ b/libs/ui/src/organisms/datatable/datatable.tsx @@ -43,7 +43,7 @@ export const DataTable = ({
- + {table.getHeaderGroups().map((headerGroup) => ( {headerGroup.headers.map((header) => (