fix: create custom hook and stepper to make multi-step modal functional
- Tambah custom hook 'use-query-state' dan implementasi stepper pada modal dengan tahapan lebih dari 1 - Hapus component ModalBackoffice karena sudah menggunakan component umum Modal
This commit is contained in:
@@ -7,36 +7,36 @@ interface IModalEditAccount {
|
|||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
handleEditAccount?: () => void;
|
handleEditAccount?: () => void;
|
||||||
currentStep?: number;
|
currentStep?: number;
|
||||||
|
nextStep: () => void;
|
||||||
|
prevStep: () => void;
|
||||||
|
resetStep: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ModalEditAccount = ({
|
const ModalEditAccount = ({
|
||||||
isOpen,
|
isOpen,
|
||||||
onClose,
|
onClose,
|
||||||
|
currentStep,
|
||||||
|
nextStep,
|
||||||
|
prevStep,
|
||||||
|
resetStep,
|
||||||
handleEditAccount,
|
handleEditAccount,
|
||||||
currentStep = 1,
|
|
||||||
}: IModalEditAccount) => {
|
}: IModalEditAccount) => {
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
className="min-w-[400px] bg-primary-50 rounded-lg p-[40px] flex flex-col gap-8 text-center"
|
className="min-w-[400px] bg-primary-50 rounded-lg p-[40px] flex flex-col gap-8 text-center"
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
onClose={onClose}
|
onClose={() => {
|
||||||
|
onClose();
|
||||||
|
resetStep();
|
||||||
|
}}
|
||||||
disableEscapeKeyDown={true}
|
disableEscapeKeyDown={true}
|
||||||
>
|
>
|
||||||
{currentStep === 1 && (
|
{currentStep === 1 && <StepOne nextStep={nextStep} onClose={onClose} />}
|
||||||
<StepOne
|
|
||||||
nextStep={() => {
|
|
||||||
console.log('Next step');
|
|
||||||
}}
|
|
||||||
onClose={onClose}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{currentStep === 2 && (
|
{currentStep === 2 && (
|
||||||
<StepTwo
|
<StepTwo
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
handleEditAccount={handleEditAccount}
|
handleEditAccount={handleEditAccount}
|
||||||
resetStep={() => {
|
resetStep={resetStep}
|
||||||
console.log('Reset step');
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Modal>
|
</Modal>
|
||||||
@@ -48,7 +48,7 @@ interface IStepOneProps {
|
|||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const StepOne = ({ nextStep, onClose }: IStepOneProps) => {
|
const StepOne = ({ nextStep }: IStepOneProps) => {
|
||||||
const [fullName, setFullName] = useState('Ahmad Wiyana');
|
const [fullName, setFullName] = useState('Ahmad Wiyana');
|
||||||
const [email, setEmail] = useState('fullname23@gmail.com');
|
const [email, setEmail] = useState('fullname23@gmail.com');
|
||||||
const [phoneNumber, setPhoneNumber] = useState('081904423804');
|
const [phoneNumber, setPhoneNumber] = useState('081904423804');
|
||||||
@@ -136,7 +136,10 @@ const StepTwo = ({ onClose, handleEditAccount, resetStep }: IStepTwoProps) => (
|
|||||||
variant="bordered"
|
variant="bordered"
|
||||||
size="lg"
|
size="lg"
|
||||||
className="w-full"
|
className="w-full"
|
||||||
onClick={resetStep}
|
onClick={() => {
|
||||||
|
onClose();
|
||||||
|
resetStep();
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
Batal
|
Batal
|
||||||
</Button>
|
</Button>
|
||||||
@@ -146,6 +149,8 @@ const StepTwo = ({ onClose, handleEditAccount, resetStep }: IStepTwoProps) => (
|
|||||||
className="w-full"
|
className="w-full"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
handleEditAccount && handleEditAccount();
|
handleEditAccount && handleEditAccount();
|
||||||
|
onClose();
|
||||||
|
resetStep();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Update
|
Update
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
RowSelectionState,
|
RowSelectionState,
|
||||||
} from '@tanstack/react-table';
|
} from '@tanstack/react-table';
|
||||||
import ModalEditAccount from './_components/modal-edit-account';
|
import ModalEditAccount from './_components/modal-edit-account';
|
||||||
|
import { useQueryState } from '../../hook/use-query-state';
|
||||||
|
|
||||||
interface Account {
|
interface Account {
|
||||||
id: number;
|
id: number;
|
||||||
@@ -39,6 +40,17 @@ const mockData: Account[] = Array.from({ length: 90 }, (_, i) => ({
|
|||||||
export const Components: FC = (): ReactElement => {
|
export const Components: FC = (): ReactElement => {
|
||||||
const [showModalEditAccount, setShowModalEditAccount] = useState(false);
|
const [showModalEditAccount, setShowModalEditAccount] = useState(false);
|
||||||
|
|
||||||
|
const {
|
||||||
|
step: currentStep,
|
||||||
|
nextStep,
|
||||||
|
prevStep,
|
||||||
|
resetStep,
|
||||||
|
} = useQueryState('step', {
|
||||||
|
defaultValue: 1,
|
||||||
|
maxValue: 2,
|
||||||
|
minValue: 1,
|
||||||
|
});
|
||||||
|
|
||||||
const [pagination, setPagination] = React.useState<PaginationState>({
|
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||||
pageIndex: 0,
|
pageIndex: 0,
|
||||||
pageSize: 9,
|
pageSize: 9,
|
||||||
@@ -166,11 +178,15 @@ export const Components: FC = (): ReactElement => {
|
|||||||
|
|
||||||
{/* Modal Edit Account */}
|
{/* Modal Edit Account */}
|
||||||
<ModalEditAccount
|
<ModalEditAccount
|
||||||
|
currentStep={currentStep}
|
||||||
isOpen={showModalEditAccount}
|
isOpen={showModalEditAccount}
|
||||||
onClose={() => setShowModalEditAccount(false)}
|
onClose={() => setShowModalEditAccount(false)}
|
||||||
handleEditAccount={() => {
|
handleEditAccount={() => {
|
||||||
console.log('Account updated');
|
console.log('Account updated');
|
||||||
}}
|
}}
|
||||||
|
nextStep={nextStep}
|
||||||
|
prevStep={prevStep}
|
||||||
|
resetStep={resetStep}
|
||||||
/>
|
/>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -6,49 +6,47 @@ interface IModalAddItem {
|
|||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
handleAddItem: () => void;
|
handleAddItem: () => void;
|
||||||
currentStep?: number;
|
currentStep?: number;
|
||||||
|
nextStep: () => void;
|
||||||
|
prevStep: () => void;
|
||||||
|
resetStep: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ModalAddItem = ({
|
const ModalAddItem = ({
|
||||||
isOpen,
|
isOpen,
|
||||||
onClose,
|
onClose,
|
||||||
|
currentStep,
|
||||||
|
nextStep,
|
||||||
|
resetStep,
|
||||||
handleAddItem,
|
handleAddItem,
|
||||||
currentStep = 1,
|
|
||||||
}: IModalAddItem) => {
|
}: IModalAddItem) => {
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
className="min-w-[400px] bg-primary-50 rounded-lg p-[40px] flex flex-col gap-8 text-center"
|
className="min-w-[400px] bg-primary-50 rounded-lg p-[40px] flex flex-col gap-8 text-center"
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
onClose={onClose}
|
onClose={() => {
|
||||||
|
onClose();
|
||||||
|
resetStep();
|
||||||
|
}}
|
||||||
disableEscapeKeyDown={true}
|
disableEscapeKeyDown={true}
|
||||||
>
|
>
|
||||||
{currentStep === 1 && (
|
{currentStep === 1 && <StepOne nextStep={nextStep} onClose={onClose} />}
|
||||||
<StepOne
|
|
||||||
nextStep={() => {
|
|
||||||
console.log('Next step');
|
|
||||||
}}
|
|
||||||
onClose={onClose}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{currentStep === 2 && (
|
{currentStep === 2 && (
|
||||||
<StepTwo
|
<StepTwo
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
handleAddItem={handleAddItem}
|
handleAddItem={handleAddItem}
|
||||||
resetStep={() => {
|
resetStep={resetStep}
|
||||||
console.log('Reset step');
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: Functional stepper
|
|
||||||
interface IStepOneProps {
|
interface IStepOneProps {
|
||||||
nextStep: () => void;
|
nextStep: () => void;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const StepOne = ({ nextStep, onClose }: IStepOneProps) => (
|
const StepOne = ({ nextStep }: IStepOneProps) => (
|
||||||
<>
|
<>
|
||||||
<Modal.Header>
|
<Modal.Header>
|
||||||
<h2 className="text-p1 font-semibold text-primary-500 mb-3">
|
<h2 className="text-p1 font-semibold text-primary-500 mb-3">
|
||||||
@@ -83,7 +81,7 @@ const StepOne = ({ nextStep, onClose }: IStepOneProps) => (
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button variant="primary" size="lg" className="w-full">
|
<Button variant="primary" size="lg" className="w-full" onClick={nextStep}>
|
||||||
Tambahkan Item
|
Tambahkan Item
|
||||||
</Button>
|
</Button>
|
||||||
</Modal.Content>
|
</Modal.Content>
|
||||||
@@ -92,8 +90,8 @@ const StepOne = ({ nextStep, onClose }: IStepOneProps) => (
|
|||||||
|
|
||||||
interface IStepTwoProps {
|
interface IStepTwoProps {
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
resetStep: () => void;
|
|
||||||
handleAddItem?: () => void;
|
handleAddItem?: () => void;
|
||||||
|
resetStep: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const StepTwo = ({ onClose, handleAddItem, resetStep }: IStepTwoProps) => (
|
const StepTwo = ({ onClose, handleAddItem, resetStep }: IStepTwoProps) => (
|
||||||
@@ -112,7 +110,10 @@ const StepTwo = ({ onClose, handleAddItem, resetStep }: IStepTwoProps) => (
|
|||||||
variant="bordered"
|
variant="bordered"
|
||||||
size="lg"
|
size="lg"
|
||||||
className="w-full"
|
className="w-full"
|
||||||
onClick={resetStep}
|
onClick={() => {
|
||||||
|
onClose();
|
||||||
|
resetStep();
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
Batal
|
Batal
|
||||||
</Button>
|
</Button>
|
||||||
@@ -122,6 +123,8 @@ const StepTwo = ({ onClose, handleAddItem, resetStep }: IStepTwoProps) => (
|
|||||||
className="w-full"
|
className="w-full"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
handleAddItem && handleAddItem();
|
handleAddItem && handleAddItem();
|
||||||
|
onClose();
|
||||||
|
resetStep();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Tambahkan
|
Tambahkan
|
||||||
|
|||||||
@@ -6,35 +6,35 @@ interface IModalEditItem {
|
|||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
handleEditItem?: () => void;
|
handleEditItem?: () => void;
|
||||||
currentStep?: number;
|
currentStep?: number;
|
||||||
|
nextStep: () => void;
|
||||||
|
prevStep: () => void;
|
||||||
|
resetStep: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ModalEditItem = ({
|
const ModalEditItem = ({
|
||||||
isOpen,
|
isOpen,
|
||||||
onClose,
|
onClose,
|
||||||
|
currentStep,
|
||||||
|
nextStep,
|
||||||
|
resetStep,
|
||||||
handleEditItem,
|
handleEditItem,
|
||||||
currentStep = 1,
|
|
||||||
}: IModalEditItem) => {
|
}: IModalEditItem) => {
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
className="min-w-[400px] bg-primary-50 rounded-lg p-[40px] flex flex-col gap-8 text-center"
|
className="min-w-[400px] bg-primary-50 rounded-lg p-[40px] flex flex-col gap-8 text-center"
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
onClose={onClose}
|
onClose={() => {
|
||||||
|
onClose();
|
||||||
|
resetStep();
|
||||||
|
}}
|
||||||
disableEscapeKeyDown={true}
|
disableEscapeKeyDown={true}
|
||||||
>
|
>
|
||||||
{currentStep === 1 && (
|
{currentStep === 1 && <StepOne nextStep={nextStep} onClose={onClose} />}
|
||||||
<StepOne
|
|
||||||
nextStep={() => {
|
|
||||||
console.log('Next step');
|
|
||||||
}}
|
|
||||||
onClose={onClose}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{currentStep === 2 && (
|
{currentStep === 2 && (
|
||||||
<StepTwo
|
<StepTwo
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
resetStep={() => {
|
handleEditItem={handleEditItem}
|
||||||
console.log('Reset step');
|
resetStep={resetStep}
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Modal>
|
</Modal>
|
||||||
@@ -46,7 +46,7 @@ interface IStepOneProps {
|
|||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const StepOne = ({ nextStep, onClose }: IStepOneProps) => (
|
const StepOne = ({ nextStep }: IStepOneProps) => (
|
||||||
<>
|
<>
|
||||||
<Modal.Header>
|
<Modal.Header>
|
||||||
<h2 className="text-p1 font-semibold text-primary-500 mb-3">
|
<h2 className="text-p1 font-semibold text-primary-500 mb-3">
|
||||||
@@ -110,7 +110,10 @@ const StepTwo = ({ onClose, handleEditItem, resetStep }: IStepTwoProps) => (
|
|||||||
variant="bordered"
|
variant="bordered"
|
||||||
size="lg"
|
size="lg"
|
||||||
className="w-full"
|
className="w-full"
|
||||||
onClick={resetStep}
|
onClick={() => {
|
||||||
|
onClose();
|
||||||
|
resetStep();
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
Batal
|
Batal
|
||||||
</Button>
|
</Button>
|
||||||
@@ -120,6 +123,8 @@ const StepTwo = ({ onClose, handleEditItem, resetStep }: IStepTwoProps) => (
|
|||||||
className="w-full"
|
className="w-full"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
handleEditItem && handleEditItem();
|
handleEditItem && handleEditItem();
|
||||||
|
onClose();
|
||||||
|
resetStep();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Update
|
Update
|
||||||
|
|||||||
@@ -10,12 +10,24 @@ import { FC, Fragment, ReactElement, useState } from 'react';
|
|||||||
import ModalAddItem from './_components/modal-add-item';
|
import ModalAddItem from './_components/modal-add-item';
|
||||||
import ModalEditItem from './_components/modal-edit-item';
|
import ModalEditItem from './_components/modal-edit-item';
|
||||||
import ModalDeleteItem from './_components/modal-delete-item';
|
import ModalDeleteItem from './_components/modal-delete-item';
|
||||||
|
import { useQueryState } from '../../hook/use-query-state';
|
||||||
|
|
||||||
export const Components: FC = (): ReactElement => {
|
export const Components: FC = (): ReactElement => {
|
||||||
const [showModalAddItem, setShowModalAddItem] = useState(false);
|
const [showModalAddItem, setShowModalAddItem] = useState(false);
|
||||||
const [showModalEditItem, setShowModalEditItem] = useState(false);
|
const [showModalEditItem, setShowModalEditItem] = useState(false);
|
||||||
const [showModalDeleteItem, setShowModalDeleteItem] = useState(false);
|
const [showModalDeleteItem, setShowModalDeleteItem] = useState(false);
|
||||||
|
|
||||||
|
const {
|
||||||
|
step: currentStep,
|
||||||
|
nextStep,
|
||||||
|
prevStep,
|
||||||
|
resetStep,
|
||||||
|
} = useQueryState('step', {
|
||||||
|
defaultValue: 1,
|
||||||
|
maxValue: 2,
|
||||||
|
minValue: 1,
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<main className="w-full px-[48px] py-[40px] flex flex-col gap-8">
|
<main className="w-full px-[48px] py-[40px] flex flex-col gap-8">
|
||||||
@@ -159,20 +171,28 @@ export const Components: FC = (): ReactElement => {
|
|||||||
|
|
||||||
{/* Modal Add Item */}
|
{/* Modal Add Item */}
|
||||||
<ModalAddItem
|
<ModalAddItem
|
||||||
|
currentStep={currentStep}
|
||||||
isOpen={showModalAddItem}
|
isOpen={showModalAddItem}
|
||||||
onClose={() => setShowModalAddItem(false)}
|
onClose={() => setShowModalAddItem(false)}
|
||||||
handleAddItem={() => {
|
handleAddItem={() => {
|
||||||
console.log('Item added');
|
console.log('Item added');
|
||||||
}}
|
}}
|
||||||
|
nextStep={nextStep}
|
||||||
|
prevStep={prevStep}
|
||||||
|
resetStep={resetStep}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Modal Edit Item */}
|
{/* Modal Edit Item */}
|
||||||
<ModalEditItem
|
<ModalEditItem
|
||||||
|
currentStep={currentStep}
|
||||||
isOpen={showModalEditItem}
|
isOpen={showModalEditItem}
|
||||||
onClose={() => setShowModalEditItem(false)}
|
onClose={() => setShowModalEditItem(false)}
|
||||||
handleEditItem={() => {
|
handleEditItem={() => {
|
||||||
console.log('Item edited');
|
console.log('Item edited');
|
||||||
}}
|
}}
|
||||||
|
nextStep={nextStep}
|
||||||
|
prevStep={prevStep}
|
||||||
|
resetStep={resetStep}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Modal Delete Item */}
|
{/* Modal Delete Item */}
|
||||||
|
|||||||
@@ -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<number>(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,
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -1 +0,0 @@
|
|||||||
export * from './modal-backoffice';
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
import { ReactElement, ReactNode } from 'react';
|
|
||||||
|
|
||||||
interface ModalBackofficeProps {
|
|
||||||
children: ReactNode;
|
|
||||||
className?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ModalBackoffice = ({
|
|
||||||
children,
|
|
||||||
className,
|
|
||||||
...rest
|
|
||||||
}: ModalBackofficeProps): ReactElement => {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={`w-[400px] bg-primary-50 rounded-lg p-[40px] gap-[32px] ${
|
|
||||||
className || ''
|
|
||||||
}`}
|
|
||||||
{...rest}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ModalBackoffice;
|
|
||||||
@@ -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<typeof ModalBackoffice> = {
|
|
||||||
title: 'Organisms/Modal Backoffice',
|
|
||||||
component: ModalBackoffice,
|
|
||||||
tags: ['autodocs'],
|
|
||||||
parameters: {
|
|
||||||
layout: 'centered',
|
|
||||||
},
|
|
||||||
} satisfies Meta<typeof ModalBackoffice>;
|
|
||||||
|
|
||||||
export default meta;
|
|
||||||
type Story = StoryObj<typeof ModalBackoffice>;
|
|
||||||
|
|
||||||
export const Default: Story = {
|
|
||||||
args: {
|
|
||||||
// isOpen: true,
|
|
||||||
// onClose: () => console.log('Modal closed'),
|
|
||||||
// title: 'Sample Modal',
|
|
||||||
children: (
|
|
||||||
<>
|
|
||||||
<h2 className="text-p1 font-semibold text-primary-500 mb-3">
|
|
||||||
Modal Title
|
|
||||||
</h2>
|
|
||||||
<p className="text-p3 text-neutral-400">
|
|
||||||
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?
|
|
||||||
</p>
|
|
||||||
<div className="flex flex-col gap-4"></div>
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const TambahItemGacha: Story = {
|
|
||||||
args: {
|
|
||||||
children: (
|
|
||||||
<div className="flex flex-col gap-8">
|
|
||||||
<div>
|
|
||||||
<h2 className="text-p1 font-semibold text-primary-500 mb-3">
|
|
||||||
Tambah Item Gacha
|
|
||||||
</h2>
|
|
||||||
<p className="text-p3 text-neutral-400">
|
|
||||||
Lengkapi detail di bawah ini untuk menambahkan item gacha
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col gap-4">
|
|
||||||
<InputForm
|
|
||||||
label="Nama Hadiah"
|
|
||||||
type="text"
|
|
||||||
placeholder="Masukkan Nama Hadiah"
|
|
||||||
size="lg"
|
|
||||||
className="w-full"
|
|
||||||
/>
|
|
||||||
<InputForm
|
|
||||||
label="Chance Rate"
|
|
||||||
type="text"
|
|
||||||
placeholder="Masukkan Chance Rate"
|
|
||||||
size="lg"
|
|
||||||
className="w-full"
|
|
||||||
/>
|
|
||||||
<InputForm
|
|
||||||
label="Foto Barang"
|
|
||||||
type="text"
|
|
||||||
placeholder=".jpg, .jpeg, atau .png"
|
|
||||||
size="lg"
|
|
||||||
className="w-full"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<Button variant="primary" size="lg" className="w-full">
|
|
||||||
Tambahkan Item
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const TambahItem: Story = {
|
|
||||||
args: {
|
|
||||||
children: (
|
|
||||||
<div className="flex flex-col gap-8 text-center">
|
|
||||||
<div>
|
|
||||||
<h2 className="text-p1 font-semibold text-primary-500 mb-3">
|
|
||||||
Tambah Item
|
|
||||||
</h2>
|
|
||||||
<p className="text-p3 text-neutral-400">
|
|
||||||
Apakah kamu yakin ingin
|
|
||||||
<br /> menambahkan item ini?
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="flex gap-4">
|
|
||||||
<Button variant="bordered" size="lg" className="w-full">
|
|
||||||
Batal
|
|
||||||
</Button>
|
|
||||||
<Button variant="primary" size="lg" className="w-full">
|
|
||||||
Tambahkan
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const EditItemGacha: Story = {
|
|
||||||
args: {
|
|
||||||
children: (
|
|
||||||
<div className="flex flex-col gap-8">
|
|
||||||
<div>
|
|
||||||
<h2 className="text-p1 font-semibold text-primary-500 mb-3">
|
|
||||||
Edit Item Gacha
|
|
||||||
</h2>
|
|
||||||
<p className="text-p3 text-neutral-400">
|
|
||||||
Silakan mengubah detail dari item yang diperlukan
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col gap-4">
|
|
||||||
<InputForm
|
|
||||||
label="Nama Hadiah"
|
|
||||||
type="text"
|
|
||||||
placeholder="Masukkan Nama Hadiah"
|
|
||||||
size="lg"
|
|
||||||
className="w-full"
|
|
||||||
/>
|
|
||||||
<InputForm
|
|
||||||
label="Chance Rate"
|
|
||||||
type="text"
|
|
||||||
placeholder="Masukkan Chance Rate"
|
|
||||||
size="lg"
|
|
||||||
className="w-full"
|
|
||||||
/>
|
|
||||||
<InputForm
|
|
||||||
label="Foto Barang"
|
|
||||||
type="text"
|
|
||||||
placeholder=".jpg, .jpeg, atau .png"
|
|
||||||
size="lg"
|
|
||||||
className="w-full"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<Button variant="primary" size="lg" className="w-full">
|
|
||||||
Perbarui Item
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const UpdateItem: Story = {
|
|
||||||
args: {
|
|
||||||
children: (
|
|
||||||
<div className="flex flex-col gap-8 text-center">
|
|
||||||
<div>
|
|
||||||
<h2 className="text-p1 font-semibold text-primary-500 mb-3">
|
|
||||||
Update Item
|
|
||||||
</h2>
|
|
||||||
<p className="text-p3 text-neutral-400">
|
|
||||||
Apakah kamu yakin dengan
|
|
||||||
<br /> perubahan yang dilakukan?
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="flex gap-4">
|
|
||||||
<Button variant="bordered" size="lg" className="w-full">
|
|
||||||
Batal
|
|
||||||
</Button>
|
|
||||||
<Button variant="primary" size="lg" className="w-full">
|
|
||||||
Update
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const DeleteItem: Story = {
|
|
||||||
args: {
|
|
||||||
children: (
|
|
||||||
<div className="flex flex-col gap-8 text-center">
|
|
||||||
<img
|
|
||||||
src="/chibi-delete.webp"
|
|
||||||
alt="Delete item?"
|
|
||||||
width={148}
|
|
||||||
className="self-center"
|
|
||||||
/>
|
|
||||||
<div>
|
|
||||||
<h2 className="text-p1 font-semibold text-danger-500 mb-3">
|
|
||||||
Delete Item
|
|
||||||
</h2>
|
|
||||||
<p className="text-p3 text-neutral-400">
|
|
||||||
Apakah kamu yakin untuk menghapus item ini?
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="flex gap-4">
|
|
||||||
<Button variant="secondary" size="lg" className="w-full">
|
|
||||||
Batal Hapus
|
|
||||||
</Button>
|
|
||||||
<Button variant="danger" size="lg" className="w-full">
|
|
||||||
Hapus Item
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const EditDataAkun: Story = {
|
|
||||||
args: {
|
|
||||||
children: (
|
|
||||||
<div className="flex flex-col gap-8">
|
|
||||||
<h2 className="text-p1 font-semibold text-primary-500 mb-3">
|
|
||||||
Edit Data Akun
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<div className="flex flex-col gap-4">
|
|
||||||
<InputForm
|
|
||||||
label="Nama Lengkap"
|
|
||||||
type="text"
|
|
||||||
placeholder="Masukkan Nama Lengkap"
|
|
||||||
value="Ahmad Wiyana"
|
|
||||||
size="lg"
|
|
||||||
className="w-full"
|
|
||||||
/>
|
|
||||||
<InputForm
|
|
||||||
label="Email"
|
|
||||||
type="text"
|
|
||||||
placeholder="Masukkan Email"
|
|
||||||
value="fullname23@gmail.com"
|
|
||||||
size="lg"
|
|
||||||
className="w-full"
|
|
||||||
/>
|
|
||||||
<InputForm
|
|
||||||
label="Nomor Telepon"
|
|
||||||
type="text"
|
|
||||||
placeholder="Masukkan Nomor Telepon"
|
|
||||||
value="081904423804"
|
|
||||||
size="lg"
|
|
||||||
className="w-full"
|
|
||||||
/>
|
|
||||||
<InputForm
|
|
||||||
label="Alamat"
|
|
||||||
type="text"
|
|
||||||
placeholder="Masukkan Alamat"
|
|
||||||
value="Jl. Pantai Cibaduyut Indah"
|
|
||||||
size="lg"
|
|
||||||
className="w-full"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<Button variant="primary" size="lg" className="w-full">
|
|
||||||
Perbarui Data
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const UpdateDataAkun: Story = {
|
|
||||||
args: {
|
|
||||||
children: (
|
|
||||||
<div className="flex flex-col gap-8 text-center">
|
|
||||||
<div>
|
|
||||||
<h2 className="text-p1 font-semibold text-primary-500 mb-3">
|
|
||||||
Update Data
|
|
||||||
</h2>
|
|
||||||
<p className="text-p3 text-neutral-400">
|
|
||||||
Apakah kamu yakin dengan
|
|
||||||
<br /> perubahan yang dilakukan?
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="flex gap-4">
|
|
||||||
<Button variant="bordered" size="lg" className="w-full">
|
|
||||||
Batal
|
|
||||||
</Button>
|
|
||||||
<Button variant="primary" size="lg" className="w-full">
|
|
||||||
Update
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ValidasiTransaksi: Story = {
|
|
||||||
args: {
|
|
||||||
children: (
|
|
||||||
<div className="flex flex-col gap-8">
|
|
||||||
<h2 className="text-p1 font-semibold text-primary-500 ">
|
|
||||||
Validasi Transaksi
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<InputForm
|
|
||||||
label="Nomor Transaksi"
|
|
||||||
type="text"
|
|
||||||
placeholder="Masukkan Nomor Transaksi"
|
|
||||||
value="2502133Y9AFVBO"
|
|
||||||
size="lg"
|
|
||||||
className="w-full"
|
|
||||||
/>
|
|
||||||
<div className="flex gap-4">
|
|
||||||
<Button
|
|
||||||
variant="bordered"
|
|
||||||
size="lg"
|
|
||||||
className="w-full border-danger-500 text-danger-500 hover:border-danger-700 hover:text-danger-700"
|
|
||||||
>
|
|
||||||
Tidak Valid
|
|
||||||
</Button>
|
|
||||||
<Button variant="primary" size="lg" className="w-full">
|
|
||||||
Valid
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ProsesDelivery: Story = {
|
|
||||||
args: {
|
|
||||||
children: (
|
|
||||||
<div className="flex flex-col gap-8">
|
|
||||||
<div>
|
|
||||||
<h2 className="text-p1 font-semibold text-primary-500 mb-3">
|
|
||||||
Delivery Process
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<p className="text-p3 text-neutral-400">
|
|
||||||
Lakukan pengiriman hadiah gacha untuk pengguna di bawah ini, jika
|
|
||||||
sudah ubah status menjadi “Delivered”.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex flex-col gap-4">
|
|
||||||
<InputForm
|
|
||||||
label="Nama Lengkap"
|
|
||||||
type="text"
|
|
||||||
placeholder="Masukkan Nama Lengkap"
|
|
||||||
value="Ahmad Wiyana"
|
|
||||||
size="lg"
|
|
||||||
className="w-full"
|
|
||||||
/>
|
|
||||||
<InputForm
|
|
||||||
label="Item yang didapatkan"
|
|
||||||
type="text"
|
|
||||||
placeholder="Masukkan Nama Item"
|
|
||||||
value="Lanyard + ID Card"
|
|
||||||
size="lg"
|
|
||||||
className="w-full"
|
|
||||||
/>
|
|
||||||
<InputForm
|
|
||||||
label="Alamat Pengiriman"
|
|
||||||
type="text"
|
|
||||||
placeholder="Masukkan Alamat Pengiriman"
|
|
||||||
value="Jl. Pantai Cibaduyut Indah"
|
|
||||||
size="lg"
|
|
||||||
className="w-full"
|
|
||||||
/>
|
|
||||||
<InputForm
|
|
||||||
label="Status"
|
|
||||||
type="text"
|
|
||||||
placeholder="Isi Status Pengiriman"
|
|
||||||
value="Lanyard + ID Card"
|
|
||||||
size="lg"
|
|
||||||
className="w-full"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<Button variant="primary" size="lg" className="w-full">
|
|
||||||
Tambahkan Item
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
Reference in New Issue
Block a user