diff --git a/apps/backoffice/src/app/accounts/_components/modal-edit-account.tsx b/apps/backoffice/src/app/accounts/_components/modal-edit-account.tsx new file mode 100644 index 0000000..adb382a --- /dev/null +++ b/apps/backoffice/src/app/accounts/_components/modal-edit-account.tsx @@ -0,0 +1,162 @@ +import { Button } from '@imphnen-frontend-service/ui/atoms'; +import { InputForm, Modal } from '@imphnen-frontend-service/ui/molecules'; +import { useState } from 'react'; + +interface IModalEditAccount { + isOpen: boolean; + onClose: () => void; + handleEditAccount?: () => void; + currentStep?: number; + nextStep: () => void; + prevStep: () => void; + resetStep: () => void; +} + +const ModalEditAccount = ({ + isOpen, + onClose, + currentStep, + nextStep, + prevStep, + resetStep, + handleEditAccount, +}: IModalEditAccount) => { + return ( + { + onClose(); + resetStep(); + }} + disableEscapeKeyDown={true} + > + {currentStep === 1 && } + {currentStep === 2 && ( + + )} + + ); +}; + +interface IStepOneProps { + nextStep: () => void; + onClose: () => void; +} + +const StepOne = ({ nextStep }: IStepOneProps) => { + const [fullName, setFullName] = useState('Ahmad Wiyana'); + const [email, setEmail] = useState('fullname23@gmail.com'); + const [phoneNumber, setPhoneNumber] = useState('081904423804'); + const [address, setAddress] = useState('Jl. Pantai Cibaduyut Indah'); + + return ( + <> + +

+ Edit Data Akun +

+
+ +
+ setFullName(e.target.value)} + size="lg" + className="w-full" + /> + setEmail(e.target.value)} + size="lg" + className="w-full" + /> + setPhoneNumber(e.target.value)} + size="lg" + className="w-full" + /> + setAddress(e.target.value)} + size="lg" + className="w-full" + /> +
+ + +
+ + ); +}; + +interface IStepTwoProps { + onClose: () => void; + handleEditAccount?: () => void; + resetStep: () => void; +} + +const StepTwo = ({ onClose, handleEditAccount, resetStep }: IStepTwoProps) => ( + <> + +

+ Update Data +

+

+ Apakah kamu yakin dengan +
perubahan yang dilakukan? +

+
+ + + + + +); + +export default ModalEditAccount; diff --git a/apps/backoffice/src/app/accounts/page.tsx b/apps/backoffice/src/app/accounts/page.tsx index 404b462..e762610 100644 --- a/apps/backoffice/src/app/accounts/page.tsx +++ b/apps/backoffice/src/app/accounts/page.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; -import { FC, ReactElement, useState } from 'react'; +import { FC, Fragment, ReactElement, useState } from 'react'; import { FilterOutlined, SearchOutlined, @@ -17,6 +17,8 @@ import { useReactTable, RowSelectionState, } from '@tanstack/react-table'; +import ModalEditAccount from './_components/modal-edit-account'; +import { useQueryState } from '../../hook/use-query-state'; interface Account { id: number; @@ -35,65 +37,20 @@ const mockData: Account[] = Array.from({ length: 90 }, (_, i) => ({ address: 'Jl. Pantai Cibaduyut Indah', })); -const columns: ColumnDef[] = [ - { - id: 'select', - header: ({ table }) => ( - - ), - cell: ({ row }) => ( - - ), - }, - { - header: 'No', - accessorKey: 'id', - }, - { - header: 'Nama Lengkap', - accessorKey: 'name', - }, - { - header: 'Email', - accessorKey: 'email', - }, - { - header: 'Nomor Telp', - accessorKey: 'phone', - }, - { - header: 'Alamat Pengiriman', - accessorKey: 'address', - }, - { - header: 'Action', - cell: ({ row }) => ( - - ), - }, -]; - export const Components: FC = (): ReactElement => { + const [showModalEditAccount, setShowModalEditAccount] = 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, @@ -102,6 +59,64 @@ export const Components: FC = (): ReactElement => { const [rowSelection, setRowSelection] = React.useState({}); const [showFilter, setShowFilter] = useState(false); + const columns: ColumnDef[] = [ + { + id: 'select', + header: ({ table }) => ( + + ), + cell: ({ row }) => ( + + ), + }, + { + header: 'No', + accessorKey: 'id', + }, + { + header: 'Nama Lengkap', + accessorKey: 'name', + }, + { + header: 'Email', + accessorKey: 'email', + }, + { + header: 'Nomor Telp', + accessorKey: 'phone', + }, + { + header: 'Alamat Pengiriman', + accessorKey: 'address', + }, + { + header: 'Action', + cell: ({ row }) => ( + + ), + }, + ]; + const table = useReactTable({ data: mockData, columns, @@ -119,49 +134,61 @@ export const Components: FC = (): ReactElement => { }); return ( -
- {/* Header */} -
-

Data Akun

-
- - {/* Account Table Section */} -
- {/* Search and Filter */} -
-
- -
- + +
+ {/* Header */} +
+

Data Akun

+
+ {/* Account Table Section */} +
+ {/* Search and Filter */} +
+
+ +
+ +
+
+
+ + {showFilter && ( +
+ setShowFilter(false)} /> +
+ )}
+ {/* Table */} + +
+
-
- - {showFilter && ( -
- setShowFilter(false)} /> -
- )} -
-
- - {/* Table */} - -
-
+ {/* Modal Edit Account */} + setShowModalEditAccount(false)} + handleEditAccount={() => { + console.log('Account updated'); + }} + nextStep={nextStep} + prevStep={prevStep} + resetStep={resetStep} + /> + ); }; diff --git a/apps/backoffice/src/app/dashboard/_components/modal-add-item.tsx b/apps/backoffice/src/app/dashboard/_components/modal-add-item.tsx new file mode 100644 index 0000000..85c92b4 --- /dev/null +++ b/apps/backoffice/src/app/dashboard/_components/modal-add-item.tsx @@ -0,0 +1,136 @@ +import { Button } from '@imphnen-frontend-service/ui/atoms'; +import { InputForm, Modal } from '@imphnen-frontend-service/ui/molecules'; + +interface IModalAddItem { + isOpen: boolean; + onClose: () => void; + handleAddItem: () => void; + 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) => ( + <> + +

+ Tambah Item Gacha +

+

+ Lengkapi detail di bawah ini untuk menambahkan item gacha +

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

+ Tambah Item +

+

+ Apakah kamu yakin ingin +
menambahkan item ini? +

+
+ + + + + +); + +export default ModalAddItem; diff --git a/apps/backoffice/src/app/dashboard/_components/modal-delete-item.tsx b/apps/backoffice/src/app/dashboard/_components/modal-delete-item.tsx new file mode 100644 index 0000000..3663ee0 --- /dev/null +++ b/apps/backoffice/src/app/dashboard/_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/dashboard/_components/modal-edit-item.tsx b/apps/backoffice/src/app/dashboard/_components/modal-edit-item.tsx new file mode 100644 index 0000000..8e88a89 --- /dev/null +++ b/apps/backoffice/src/app/dashboard/_components/modal-edit-item.tsx @@ -0,0 +1,136 @@ +import { Button } from '@imphnen-frontend-service/ui/atoms'; +import { InputForm, Modal } from '@imphnen-frontend-service/ui/molecules'; + +interface IModalEditItem { + isOpen: boolean; + onClose: () => void; + handleEditItem?: () => void; + currentStep?: number; + nextStep: () => void; + prevStep: () => void; + resetStep: () => void; +} + +const ModalEditItem = ({ + isOpen, + onClose, + currentStep, + nextStep, + resetStep, + handleEditItem, +}: IModalEditItem) => { + return ( + { + onClose(); + resetStep(); + }} + disableEscapeKeyDown={true} + > + {currentStep === 1 && } + {currentStep === 2 && ( + + )} + + ); +}; + +interface IStepOneProps { + nextStep: () => void; + onClose: () => void; +} + +const StepOne = ({ nextStep }: IStepOneProps) => ( + <> + +

+ Edit Item Gacha +

+

+ Silakan mengubah detail dari item yang diperlukan +

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

+ Update Item +

+

+ Apakah kamu yakin dengan +
perubahan yang dilakukan? +

+
+ + + + + +); + +export default ModalEditItem; diff --git a/apps/backoffice/src/app/dashboard/page.tsx b/apps/backoffice/src/app/dashboard/page.tsx index 2567e8c..d337e53 100644 --- a/apps/backoffice/src/app/dashboard/page.tsx +++ b/apps/backoffice/src/app/dashboard/page.tsx @@ -6,139 +6,204 @@ import { UserSwitchOutlined, } from '@ant-design/icons'; import { Button } from '@imphnen-frontend-service/ui/atoms'; -import { FC, ReactElement } from 'react'; +import { FC, Fragment, ReactElement, useState } from 'react'; +import ModalAddItem from './_components/modal-add-item'; +import ModalEditItem from './_components/modal-edit-item'; +import ModalDeleteItem from './_components/modal-delete-item'; +import { useQueryState } from '../../hook/use-query-state'; export const Components: FC = (): ReactElement => { + const [showModalAddItem, setShowModalAddItem] = useState(false); + const [showModalEditItem, setShowModalEditItem] = useState(false); + const [showModalDeleteItem, setShowModalDeleteItem] = useState(false); + + const { + step: currentStep, + nextStep, + prevStep, + resetStep, + } = useQueryState('step', { + defaultValue: 1, + maxValue: 2, + minValue: 1, + }); + return ( -
- {/* Dashboard Header */} -
-

Dashboard

-
+ +
+ {/* Dashboard Header */} +
+

Dashboard

+
-
-
- {/* Summary Section */} -
-

- Summary -

-
- {/* Participants */} -
-
- -
-
-

1000

-

Participants

-
-
- - {/* Roll and Reroll */} -
-
- -
-
-

1000

-

- Roll and Reroll -

-
-
- - {/* Redeem */} -
-
- -
-
-

1000

-

Redeem

-
-
- - {/* Inactive Users */} -
-
- -
-
-

1000

-

Inactive Users

-
-
-
-
- - {/* Gacha Items Section */} -
-
-

- Gacha Items +
+
+ {/* Summary Section */} +
+

+ Summary

- -
+
+ {/* Participants */} +
+
+ +
+
+

1000

+

Participants

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

1000

+

+ Roll and Reroll +

+
+
+ + {/* Redeem */} +
+
+ +
+
+

1000

+

Redeem

+
+
+ + {/* Inactive Users */} +
+
+ +
+
+

1000

+

+ Inactive Users +

+
+
+
+

+ + {/* Gacha Items Section */} +
+
+

+ Gacha Items +

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

+ Lanyard IMPHNEN +

+
+ Prize {item} + Chance Rate: (0.1%) +
+
+
+ +
-
- - -
+ + {/* Lebih baik gunakan gambar yang sudah di-clip dengan size height: 78px daripada hard-code object-position dan margin */} + Lanyard IMPHNEN
+ ))} +
+
+
- {/* Lebih baik gunakan gambar yang sudah di-clip dengan size height: 78px daripada hard-code object-position dan margin */} - Lanyard IMPHNEN -
- ))} - - + {/* Right-side illustration */} + +
- {/* Right-side illustration */} - - -
+ {/* Modal Add Item */} + setShowModalAddItem(false)} + handleAddItem={() => { + console.log('Item added'); + }} + nextStep={nextStep} + prevStep={prevStep} + resetStep={resetStep} + /> + + {/* Modal Edit Item */} + setShowModalEditItem(false)} + handleEditItem={() => { + console.log('Item edited'); + }} + nextStep={nextStep} + prevStep={prevStep} + resetStep={resetStep} + /> + + {/* Modal Delete Item */} + setShowModalDeleteItem(false)} + handleDeleteItem={() => { + console.log('Item deleted'); + }} + /> + ); }; diff --git a/apps/backoffice/src/app/prizes/_components/modal-process-item.tsx b/apps/backoffice/src/app/prizes/_components/modal-process-item.tsx new file mode 100644 index 0000000..19a907b --- /dev/null +++ b/apps/backoffice/src/app/prizes/_components/modal-process-item.tsx @@ -0,0 +1,84 @@ +import { Button } from '@imphnen-frontend-service/ui/atoms'; +import { InputForm, Modal } from '@imphnen-frontend-service/ui/molecules'; + +interface IModalProcessDelivery { + isOpen: boolean; + onClose: () => void; + handleProcessDelivery?: () => void; +} + +const ModalProcessDelivery = ({ + isOpen, + onClose, + handleProcessDelivery, +}: IModalProcessDelivery) => { + return ( + + +

+ Delivery Process +

+

+ Lakukan pengiriman hadiah gacha untuk pengguna di bawah ini, jika + sudah ubah status menjadi “Delivered”. +

+
+ +
+ + + + +
+ + +
+
+ ); +}; + +export default ModalProcessDelivery; diff --git a/apps/backoffice/src/app/prizes/page.tsx b/apps/backoffice/src/app/prizes/page.tsx index f7828c4..1e15a2a 100644 --- a/apps/backoffice/src/app/prizes/page.tsx +++ b/apps/backoffice/src/app/prizes/page.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; -import { FC, ReactElement, useState } from 'react'; +import { FC, Fragment, ReactElement, useState } from 'react'; import { FilterOutlined, SearchOutlined, @@ -17,13 +17,15 @@ import { useReactTable, RowSelectionState, } from '@tanstack/react-table'; +import ModalProcessDelivery from './_components/modal-process-item'; -type Status = 'valid' | 'invalid' | 'unchecked'; +type OrderValid = 'valid' | 'invalid' | 'unchecked'; +type Status = 'undelivered' | 'delivered'; interface Prize { id: number; name: string; - orderValid: Status; + orderValid: OrderValid; items: string; address: string; status: Status; @@ -46,119 +48,16 @@ const mockData: Prize[] = Array.from({ length: 90 }, (_, i) => ({ ? 'invalid' : i % 5 === 0 ? 'unchecked' - : 'valid') as Status, + : 'valid') as OrderValid, items: items[i % items.length], address: 'Jl. Pantai Cibaduyut Indah', - status: (i % 3 === 0 - ? 'unchecked' - : i % 5 === 0 - ? 'invalid' - : 'valid') as Status, + status: (i % 3 === 0 ? 'undelivered' : 'delivered') as Status, })); -const columns: ColumnDef[] = [ - { - id: 'select', - header: ({ table }) => ( - - ), - cell: ({ row }) => ( - - ), - }, - { - header: 'No', - accessorKey: 'id', - }, - { - header: 'Nama Lengkap', - accessorKey: 'name', - }, - { - header: 'Order Valid?', - accessorKey: 'orderValid', - cell: ({ row }) => { - const status = row.original.orderValid; - const statusColors: Record = { - valid: 'bg-success-200 text-success-500', - invalid: 'bg-danger-200 text-danger-500', - unchecked: 'bg-warning-200 text-warning-900', - }; - const statusText: Record = { - valid: 'Valid', - invalid: 'Invalid', - unchecked: 'Unchecked', - }; - return ( -
- {statusText[status]} -
- ); - }, - }, - { - header: 'Items', - accessorKey: 'items', - }, - { - header: 'Alamat Pengiriman', - accessorKey: 'address', - }, - { - header: 'Status', - accessorKey: 'status', - cell: ({ row }) => { - const status = row.original.status; - const statusColors: Record = { - valid: 'bg-success-200 text-success-500', - invalid: 'bg-danger-200 text-danger-500', - unchecked: 'bg-warning-200 text-warning-900', - }; - const statusText: Record = { - valid: 'Valid', - invalid: 'Invalid', - unchecked: 'Unchecked', - }; - return ( -
- {statusText[status]} -
- ); - }, - }, - { - header: 'Action', - cell: ({ row }) => ( - - ), - }, -]; - export const Components: FC = (): ReactElement => { + const [showModalProcessDelivery, setShowModalProcessDelivery] = + useState(false); + const [pagination, setPagination] = React.useState({ pageIndex: 0, pageSize: 9, @@ -167,6 +66,111 @@ export const Components: FC = (): ReactElement => { const [rowSelection, setRowSelection] = React.useState({}); const [showFilter, setShowFilter] = useState(false); + const deliveryOptions = [ + { id: 'option1', value: 'undelivered', label: 'Undelivered' }, + { id: 'option1', value: 'delivered', label: 'Delivered' }, + ]; + + const columns: ColumnDef[] = [ + { + id: 'select', + header: ({ table }) => ( + + ), + cell: ({ row }) => ( + + ), + }, + { + header: 'No', + accessorKey: 'id', + }, + { + header: 'Nama Lengkap', + accessorKey: 'name', + }, + { + header: 'Order Valid?', + accessorKey: 'orderValid', + cell: ({ row }) => { + const status = row.original.orderValid; + const statusColors: Record = { + valid: 'bg-success-200 text-success-500', + invalid: 'bg-danger-200 text-danger-500', + unchecked: 'bg-warning-200 text-warning-900', + }; + const statusText: Record = { + valid: 'Valid', + invalid: 'Invalid', + unchecked: 'Unchecked', + }; + return ( +
+ {statusText[status]} +
+ ); + }, + }, + { + header: 'Items', + accessorKey: 'items', + }, + { + header: 'Alamat Pengiriman', + accessorKey: 'address', + }, + { + header: 'Status', + accessorKey: 'status', + cell: ({ row }) => { + const status = row.original.status; + const statusColors: Record = { + delivered: 'bg-success-200 text-success-500', + undelivered: 'bg-danger-200 text-danger-500', + }; + const statusText: Record = { + delivered: 'Delivered', + undelivered: 'Undelivered', + }; + return ( +
+ {statusText[status]} +
+ ); + }, + }, + { + header: 'Action', + cell: ({ row }) => ( + + ), + }, + ]; + const table = useReactTable({ data: mockData, columns, @@ -184,48 +188,63 @@ export const Components: FC = (): ReactElement => { }); return ( -
- {/* Header */} -
-

Data Pengiriman Hadiah

-
- - {/* Account Table Section */} -
- {/* Search and Filter */} -
-
- -
- + +
+ {/* Header */} +
+

Data Pengiriman Hadiah

+
+ {/* Account Table Section */} +
+ {/* Search and Filter */} +
+
+ +
+ +
+
+
+ + {showFilter && ( +
+ setShowFilter(false)} + onFilterChange={(value) => { + console.log('Selected filter:', value); + // Filter logic di sini + }} + /> +
+ )}
+ {/* Table */} + +
+
-
- - {showFilter && ( -
- setShowFilter(false)} /> -
- )} -
-
- - {/* Table */} - -
-
+ {/* Modal Process Delivery */} + setShowModalProcessDelivery(false)} + handleProcessDelivery={() => { + console.log('Action ketika user menekan tombol Proses Pengiriman'); + }} + /> + ); }; diff --git a/apps/backoffice/src/app/transactions/_components/modal-validate.tsx b/apps/backoffice/src/app/transactions/_components/modal-validate.tsx new file mode 100644 index 0000000..5938490 --- /dev/null +++ b/apps/backoffice/src/app/transactions/_components/modal-validate.tsx @@ -0,0 +1,61 @@ +import { Button } from '@imphnen-frontend-service/ui/atoms'; +import { InputForm, Modal } from '@imphnen-frontend-service/ui/molecules'; + +interface IModalValidate { + isOpen: boolean; + onClose: () => void; + handleValid?: () => void; + handleInvalid?: () => void; +} + +const ModalValidate = ({ + isOpen, + onClose, + handleValid, + handleInvalid, +}: IModalValidate) => { + return ( + + +

+ Validasi Transaksi +

+
+ + +
+ + +
+
+
+ ); +}; + +export default ModalValidate; diff --git a/apps/backoffice/src/app/transactions/page.tsx b/apps/backoffice/src/app/transactions/page.tsx index a03b161..f2f5070 100644 --- a/apps/backoffice/src/app/transactions/page.tsx +++ b/apps/backoffice/src/app/transactions/page.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { FC, ReactElement, useState } from 'react'; +import { FC, Fragment, ReactElement, useState } from 'react'; import { FilterOutlined, SearchOutlined, @@ -16,6 +16,7 @@ import { useReactTable, RowSelectionState, } from '@tanstack/react-table'; +import ModalValidate from './_components/modal-validate'; type TransactionStatus = 'valid' | 'invalid' | 'unchecked'; @@ -38,81 +39,9 @@ const mockTransactions: Transaction[] = Array.from({ length: 20 }, (_, i) => ({ : 'valid') as TransactionStatus, })); -const columns: ColumnDef[] = [ - { - id: 'select', - header: ({ table }) => ( - - ), - cell: ({ row }) => ( - - ), - }, - { - header: 'No', - accessorKey: 'id', - }, - { - header: 'Nama Lengkap', - accessorKey: 'name', - }, - { - header: 'Nomor Transaksi', - accessorKey: 'transactionNumber', - }, - { - header: 'Order Valid?', - accessorKey: 'status', - cell: ({ row }) => { - const status = row.original.status; - const statusColors: Record = { - valid: 'bg-success-200 text-success-500', - invalid: 'bg-danger-200 text-danger-500', - unchecked: 'bg-warning-200 text-warning-900', - }; - const statusText: Record = { - valid: 'Valid', - invalid: 'Invalid', - unchecked: 'Unchecked', - }; - return ( -
- {statusText[status]} -
- ); - }, - }, - { - header: 'Action', - cell: ({ row }) => ( - - ), - }, -]; - export const Components: FC = (): ReactElement => { + const [showModalValidate, setShowModalValidate] = useState(false); + const [pagination, setPagination] = React.useState({ pageIndex: 0, pageSize: 9, @@ -121,6 +50,86 @@ export const Components: FC = (): ReactElement => { const [rowSelection, setRowSelection] = React.useState({}); const [showFilter, setShowFilter] = useState(false); + const validationOptions = [ + { id: 'option1', value: 'unchecked', label: 'Unchecked' }, + { id: 'option2', value: 'valid', label: 'Valid' }, + { id: 'option3', value: 'invalid', label: 'Invalid' }, + ]; + + const columns: ColumnDef[] = [ + { + id: 'select', + header: ({ table }) => ( + + ), + cell: ({ row }) => ( + + ), + }, + { + header: 'No', + accessorKey: 'id', + }, + { + header: 'Nama Lengkap', + accessorKey: 'name', + }, + { + header: 'Nomor Transaksi', + accessorKey: 'transactionNumber', + }, + { + header: 'Order Valid?', + accessorKey: 'status', + cell: ({ row }) => { + const status = row.original.status; + const statusColors: Record = { + valid: 'bg-success-200 text-success-500', + invalid: 'bg-danger-200 text-danger-500', + unchecked: 'bg-warning-200 text-warning-900', + }; + const statusText: Record = { + valid: 'Valid', + invalid: 'Invalid', + unchecked: 'Unchecked', + }; + return ( +
+ {statusText[status]} +
+ ); + }, + }, + { + header: 'Action', + cell: ({ row }) => ( + + ), + }, + ]; + const table = useReactTable({ data: mockTransactions, columns, @@ -138,48 +147,67 @@ export const Components: FC = (): ReactElement => { }); return ( -
- {/* Header */} -
-

Validasi Transaksi

-
+ +
+ {/* Header */} +
+

Validasi Transaksi

+
- {/* Account Table Section */} -
- {/* Search and Filter */} -
-
- -
- + {/* Account Table Section */} +
+ {/* Search and Filter */} +
+
+ +
+ +
+
+ +
+ + {showFilter && ( +
+ setShowFilter(false)} + onFilterChange={(value) => { + console.log('Selected filter:', value); + // Filter logic di sini + }} + /> +
+ )}
-
- - {showFilter && ( -
- setShowFilter(false)} /> -
- )} -
-
- - {/* Table */} - -
-
+ {/* Table */} + + +
+ setShowModalValidate(false)} + handleValid={() => { + console.log('Action ketika user klik Valid'); + }} + handleInvalid={() => { + console.log('Action ketika user klik Tidak Valid'); + }} + /> + ); }; diff --git a/apps/backoffice/src/hook/use-query-state.tsx b/apps/backoffice/src/hook/use-query-state.tsx new file mode 100644 index 0000000..3943f95 --- /dev/null +++ b/apps/backoffice/src/hook/use-query-state.tsx @@ -0,0 +1,71 @@ +import { useCallback, useEffect, useState } from 'react'; + +interface UseQueryStateOptions { + defaultValue: number; + maxValue?: number; + minValue?: number; +} + +export const useQueryState = (key: string, options: UseQueryStateOptions) => { + const { defaultValue, maxValue = Infinity, minValue = 1 } = options; + + const getQueryParam = (param: string): string | null => { + if (typeof window === 'undefined') return null; + const searchParams = new URLSearchParams(window.location.search); + return searchParams.get(param); + }; + + const setQueryParam = (param: string, value: string) => { + const searchParams = new URLSearchParams(window.location.search); + searchParams.set(param, value); + const newUrl = `${window.location.pathname}?${searchParams.toString()}`; + window.history.replaceState(null, '', newUrl); + }; + + const initialValue = () => { + const queryValue = getQueryParam(key); + const parsedValue = queryValue ? parseInt(queryValue, 10) : defaultValue; + return Math.max(minValue, Math.min(maxValue, parsedValue)); + }; + + const [value, setValue] = useState(initialValue); + + useEffect(() => { + const handlePopState = () => { + const queryValue = getQueryParam(key); + const newValue = queryValue ? parseInt(queryValue, 10) : defaultValue; + setValue(Math.max(minValue, Math.min(maxValue, newValue))); + }; + + window.addEventListener('popstate', handlePopState); + return () => window.removeEventListener('popstate', handlePopState); + }, [key, defaultValue, minValue, maxValue]); + + const updateValue = useCallback( + (newValue: number) => { + const constrainedValue = Math.max(minValue, Math.min(maxValue, newValue)); + setValue(constrainedValue); + setQueryParam(key, constrainedValue.toString()); + }, + [key, minValue, maxValue] + ); + + const nextStep = useCallback(() => { + updateValue(value + 1); + }, [value, updateValue]); + + const prevStep = useCallback(() => { + updateValue(value - 1); + }, [value, updateValue]); + + const resetStep = useCallback(() => { + updateValue(defaultValue); + }, [defaultValue, updateValue]); + + return { + step: value, + nextStep, + prevStep, + resetStep, + }; +}; diff --git a/libs/ui/src/atoms/input/input.tsx b/libs/ui/src/atoms/input/input.tsx index 72f8575..ae0e849 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'; +type TInputType = 'text' | 'email' | 'password' | 'file'; type TInputSize = 'sm' | 'md' | 'lg'; type TInputProps = Omit< diff --git a/libs/ui/src/molecules/input-form/input-form.tsx b/libs/ui/src/molecules/input-form/input-form.tsx index 1af658d..7e5c8d3 100644 --- a/libs/ui/src/molecules/input-form/input-form.tsx +++ b/libs/ui/src/molecules/input-form/input-form.tsx @@ -7,7 +7,7 @@ import { import { Input } from '../../atoms'; import { cn } from '@imphnen-frontend-service/utils'; -type TInputType = 'text' | 'email' | 'password'; +type TInputType = 'text' | 'email' | 'password' | 'file'; type TInputSize = 'sm' | 'md' | 'lg'; type TInputFormProps = Omit< diff --git a/libs/ui/src/organisms/filter/filter.tsx b/libs/ui/src/organisms/filter/filter.tsx index 268a36c..b0119cb 100644 --- a/libs/ui/src/organisms/filter/filter.tsx +++ b/libs/ui/src/organisms/filter/filter.tsx @@ -42,13 +42,31 @@ const Radio = ({ interface FilterProps { onClose?: () => void; + options: Array<{ + id: string; + value: string; + label: string; + }>; + selectedValue?: string; + onFilterChange?: (value: string) => void; + title?: string; } -export const Filter = ({ onClose }: FilterProps) => { - const [selectedStatus, setSelectedStatus] = useState('delivered'); +export const Filter = ({ + onClose, + options, + selectedValue, + onFilterChange, + title = 'Status', +}: FilterProps) => { + const [selectedStatus, setSelectedStatus] = useState( + selectedValue || options[0]?.value || '' + ); const handleStatusChange = (e: React.ChangeEvent) => { - setSelectedStatus(e.target.value); + const newValue = e.target.value; + setSelectedStatus(newValue); + onFilterChange?.(newValue); }; return ( @@ -63,24 +81,19 @@ export const Filter = ({ onClose }: FilterProps) => {
- Status + {title}
- - + {options.map((option) => ( + + ))}
); diff --git a/libs/ui/src/organisms/modal-backoffice/index.ts b/libs/ui/src/organisms/modal-backoffice/index.ts deleted file mode 100644 index 47fe845..0000000 --- a/libs/ui/src/organisms/modal-backoffice/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './modal-backoffice'; diff --git a/libs/ui/src/organisms/modal-backoffice/modal-backoffice.tsx b/libs/ui/src/organisms/modal-backoffice/modal-backoffice.tsx deleted file mode 100644 index 5c9ba0d..0000000 --- a/libs/ui/src/organisms/modal-backoffice/modal-backoffice.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { ReactElement, ReactNode } from 'react'; - -interface ModalBackofficeProps { - children: ReactNode; - className?: string; -} - -export const ModalBackoffice = ({ - children, - className, - ...rest -}: ModalBackofficeProps): ReactElement => { - return ( -
- {children} -
- ); -}; - -export default ModalBackoffice; diff --git a/libs/ui/src/organisms/modal-backoffice/modal.stories.tsx b/libs/ui/src/organisms/modal-backoffice/modal.stories.tsx deleted file mode 100644 index 2eb38e1..0000000 --- a/libs/ui/src/organisms/modal-backoffice/modal.stories.tsx +++ /dev/null @@ -1,373 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react'; -import { ModalBackoffice } from './modal-backoffice'; -import { InputForm } from '../../molecules'; -import { Button } from '../../atoms'; - -const meta: Meta = { - title: 'Organisms/Modal Backoffice', - component: ModalBackoffice, - tags: ['autodocs'], - parameters: { - layout: 'centered', - }, -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - args: { - // isOpen: true, - // onClose: () => console.log('Modal closed'), - // title: 'Sample Modal', - children: ( - <> -

- Modal Title -

-

- Lorem ipsum dolor sit amet consectetur adipisicing elit. Quas repellat - rerum doloremque dolorem fugit architecto eos blanditiis ratione - facere? Esse dolores inventore deleniti. Dicta voluptatem dolore vel - quia amet dolorem? -

-
- - ), - }, -}; - -export const TambahItemGacha: Story = { - args: { - children: ( -
-
-

- Tambah Item Gacha -

-

- Lengkapi detail di bawah ini untuk menambahkan item gacha -

-
-
- - - -
- -
- ), - }, -}; - -export const TambahItem: Story = { - args: { - children: ( -
-
-

- Tambah Item -

-

- Apakah kamu yakin ingin -
menambahkan item ini? -

-
-
- - -
-
- ), - }, -}; - -export const EditItemGacha: Story = { - args: { - children: ( -
-
-

- Edit Item Gacha -

-

- Silakan mengubah detail dari item yang diperlukan -

-
-
- - - -
- -
- ), - }, -}; - -export const UpdateItem: Story = { - args: { - children: ( -
-
-

- Update Item -

-

- Apakah kamu yakin dengan -
perubahan yang dilakukan? -

-
-
- - -
-
- ), - }, -}; - -export const DeleteItem: Story = { - args: { - children: ( -
- Delete item? -
-

- Delete Item -

-

- Apakah kamu yakin untuk menghapus item ini? -

-
-
- - -
-
- ), - }, -}; - -export const EditDataAkun: Story = { - args: { - children: ( -
-

- Edit Data Akun -

- -
- - - - -
- -
- ), - }, -}; - -export const UpdateDataAkun: Story = { - args: { - children: ( -
-
-

- Update Data -

-

- Apakah kamu yakin dengan -
perubahan yang dilakukan? -

-
-
- - -
-
- ), - }, -}; - -export const ValidasiTransaksi: Story = { - args: { - children: ( -
-

- Validasi Transaksi -

- - -
- - -
-
- ), - }, -}; - -export const ProsesDelivery: Story = { - args: { - children: ( -
-
-

- Delivery Process -

- -

- Lakukan pengiriman hadiah gacha untuk pengguna di bawah ini, jika - sudah ubah status menjadi “Delivered”. -

-
- -
- - - - -
- -
- ), - }, -}; diff --git a/package.json b/package.json index 4a3cf8a..3c7f58b 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "gacha:prod": "serve ./dist/apps/gacha", "backoffice:dev": "nx serve backoffice", "backoffice:build": "nx build backoffice", - "backoffice:prod": "serve ./dist/apps/gacha", + "backoffice:prod": "serve ./dist/apps/backoffice", "dimentorin:dev": "nx serve dimentorin", "dimentorin:build": "nx build dimentorin", "dimentorin:prod": "serve ./dist/apps/dimentorin",