diff --git a/app/(dashboard)/assets/imagecase.svg b/app/(dashboard)/assets/imagecase.svg new file mode 100644 index 0000000..4ee65eb --- /dev/null +++ b/app/(dashboard)/assets/imagecase.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/app/(dashboard)/assets/photocase.svg b/app/(dashboard)/assets/photocase.svg new file mode 100644 index 0000000..7d566df --- /dev/null +++ b/app/(dashboard)/assets/photocase.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/app/(dashboard)/settings/_components/AddStaff.tsx b/app/(dashboard)/settings/_components/AddStaff.tsx new file mode 100644 index 0000000..1e81cda --- /dev/null +++ b/app/(dashboard)/settings/_components/AddStaff.tsx @@ -0,0 +1,310 @@ +import { + Sheet, + SheetClose, + SheetContent, + SheetTitle, + SheetTrigger, +} from '@/components/ui/sheet'; +import { CirclePlus } from 'lucide-react'; +import PhotoCase from '@/app/(dashboard)/assets/photocase.svg'; +import { ChangeEvent, useState } from 'react'; +import { FieldValues, useForm } from 'react-hook-form'; +import Image from 'next/image'; +import Checkbox from '@/components/Checkbox/Checkbox'; +import SuccesModal from '@/components/SuccesModal/SuccesModal'; + +export function AddStaff() { + const [addImage, setAddImage] = useState(PhotoCase); + const [step, setStep] = useState('form'); + const { register, handleSubmit, watch } = useForm(); + + const handleOnSubmit = (data: FieldValues) => { + data.photo = addImage; + console.log(data); + }; + + const handleImageUpload = (event: ChangeEvent) => { + const file = event.target.files?.[0]; + if (file) { + setAddImage(URL.createObjectURL(file)); + } + }; + + const handleDeleteImage = () => { + setAddImage(PhotoCase); + }; + + const nameValue = watch('password'); + + return ( + + + + + {step === 'form' ? ( + +
+
+ + Add New Staff + + +
+ {addImage && ( + staff name + )} +
+ + +
+
+

+ Delete +

+
+
+ +
+

+ Staff Information +

+
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+
+ +
+

+ Security +

+
+
+ + +
+
+ + +
+
+
+ +
+

+ Access +

+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+ +
+ + Cancel + + + +
+
+
+
+ ) : ( + + + + )} +
+ ); +} diff --git a/app/(dashboard)/settings/_components/ConfirmationCode.tsx b/app/(dashboard)/settings/_components/ConfirmationCode.tsx new file mode 100644 index 0000000..d1bbd1c --- /dev/null +++ b/app/(dashboard)/settings/_components/ConfirmationCode.tsx @@ -0,0 +1,117 @@ +import { Columns3 } from 'lucide-react'; +import React, { useRef, useState } from 'react'; +import { useForm } from 'react-hook-form'; + +type Props = { + nextPasswordStep: () => void; +}; + +const ConfirmationCode = ({ nextPasswordStep }: Props) => { + const [confirmationCode, setConfirmationCode] = useState( + Array(4).fill('') + ); + const { handleSubmit, clearErrors } = useForm(); + + const handleOnSubmit = () => { + const code = confirmationCode.join(''); + console.log('Confirmation code submitted:', code); + nextPasswordStep(); + }; + + const inputRefs = useRef<(HTMLInputElement | null)[]>([]); + + const handleChange = (index: number, value: string) => { + if (value.length > 1) { + value = value[0]; + } + const newConfirmationCode = [...confirmationCode]; + newConfirmationCode[index] = value; + setConfirmationCode(newConfirmationCode); + // Move to next input if value is entered + if (value && index < 5) { + inputRefs.current[index + 1]?.focus(); + } + // Clear any previous errors when user starts typing + clearErrors('code'); + }; + + const handleKeyDown = ( + index: number, + e: React.KeyboardEvent + ) => { + if (e.key === 'Backspace' && !confirmationCode[index] && index > 0) { + inputRefs.current[index - 1]?.focus(); + } + }; + + const handlePaste = (e: React.ClipboardEvent) => { + e.preventDefault(); + const pastedData = e.clipboardData.getData('text').slice(0, 6); + const newConfirmationCode = [...confirmationCode]; + + for (let i = 0; i < pastedData.length; i++) { + if (i < 6) { + newConfirmationCode[i] = pastedData[i]; + } + } + + setConfirmationCode(newConfirmationCode); + + // Focus the next empty input or the last input + const nextEmptyIndex = newConfirmationCode.findIndex((x) => !x); + if (nextEmptyIndex !== -1) { + inputRefs.current[nextEmptyIndex]?.focus(); + } else { + inputRefs.current[5]?.focus(); + } + }; + + const buttonDisabled = confirmationCode.some((digit) => digit === ''); + + return ( +
+
+ +
+
+

+ Enter confirmation code +

+

+ We've sent a code to{' '} + user@paystride.com +

+
+
+
+ {confirmationCode.map((digit, index) => ( + { + inputRefs.current[index] = el; + }} + type="text" + inputMode="numeric" + maxLength={1} + value={digit} + onChange={(e) => handleChange(index, e.target.value)} + onKeyDown={(e) => handleKeyDown(index, e)} + onPaste={handlePaste} + className="w-12 min-[375px]:w-16 sm:w-[90px] h-20 min-[375px]:h-24 sm:h-[120px] text-center text-[#0F172A] text-5xl font-semibold border border-[#CBD5E1] rounded-[10px] focus:border-[#324BC6] focus:outline-none" + aria-label={`Digit ${index + 1}`} + /> + ))} +
+ +
+
+ ); +}; + +export default ConfirmationCode; diff --git a/app/(dashboard)/settings/_components/EditBusiness.tsx b/app/(dashboard)/settings/_components/EditBusiness.tsx new file mode 100644 index 0000000..c56397c --- /dev/null +++ b/app/(dashboard)/settings/_components/EditBusiness.tsx @@ -0,0 +1,200 @@ +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogHeader, + AlertDialogTitle, + AlertDialogTrigger, +} from '@/components/ui/alert-dialog'; +import { Button } from '@/components/ui/button'; +import { Calendar } from '@/components/ui/calendar'; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from '@/components/ui/popover'; + +import { cn } from '@/lib/utils'; +import { BusinessFormData } from '@/types/types'; +import { format } from 'date-fns'; +import { CalendarIcon, Pen } from 'lucide-react'; +import Image from 'next/image'; +import React, { useState } from 'react'; +import { FieldValues, useForm } from 'react-hook-form'; + +type EditBusinessProps = { + onSubmit: (data: FieldValues) => void; +}; + +const EditBusiness = ({ onSubmit }: EditBusinessProps) => { + const [date, setDate] = useState(); + const [imageName, setImageName] = useState(''); + const [businessImage, setBusinessImage] = useState(null); + const { register, handleSubmit, watch } = useForm(); + + const nameValue = watch( + // 'businessPhoto' + 'businessName' + // 'memberSince', + // 'employeeNo' + ); + + const handleOnSubmit = (data: FieldValues) => { + data.businessPhoto = businessImage; + data.memberSince = date; + onSubmit(data); + console.log(data); + }; + + const handleImageUpload = (event: React.ChangeEvent) => { + const file = event.target.files?.[0]; + if (file) { + setImageName(file.name); + setBusinessImage(URL.createObjectURL(file)); // Generate local preview URL + } + }; + + return ( + + + + + + + + +
+
+
+ {businessImage ? ( +
+ {imageName} +
+ ) : ( +
+ + +
+ )} +
+
+
+ + +
+
+
+ + + + + + + + + +
+
+ + +
+
+
+
+
+ + Cancel + + + Save Changes + +
+
+
+
+
+
+ ); +}; + +export default EditBusiness; diff --git a/app/(dashboard)/settings/_components/EditInformation.tsx b/app/(dashboard)/settings/_components/EditInformation.tsx new file mode 100644 index 0000000..a4a3732 --- /dev/null +++ b/app/(dashboard)/settings/_components/EditInformation.tsx @@ -0,0 +1,186 @@ +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogHeader, + AlertDialogTitle, + AlertDialogTrigger, +} from '@/components/ui/alert-dialog'; +import { InformationFormData } from '@/types/types'; +import { Pen } from 'lucide-react'; +import React from 'react'; +import { FieldValues, useForm } from 'react-hook-form'; + +type EditInformationProps = { + onSubmit: (data: FieldValues) => void; +}; + +const EditInformation = ({ onSubmit }: EditInformationProps) => { + const { register, handleSubmit, reset } = useForm(); + + const handleOnSubmit = (data: FieldValues) => { + onSubmit(data); + console.log(data); + reset(); + }; + + return ( + + + + + + + + +
+
+

Information

+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+ +
+ + Cancel + + + Save Changes + +
+
+
+
+
+
+ ); +}; + +export default EditInformation; diff --git a/app/(dashboard)/settings/_components/General.tsx b/app/(dashboard)/settings/_components/General.tsx new file mode 100644 index 0000000..7e66ba1 --- /dev/null +++ b/app/(dashboard)/settings/_components/General.tsx @@ -0,0 +1,107 @@ +import React, { useState } from 'react'; +import EditBusiness from './EditBusiness'; +import EditInformation from './EditInformation'; +import { BusinessFormData, InformationFormData } from '@/types/types'; +import Image from 'next/image'; +import ImageCase from '@/app/(dashboard)/assets/imagecase.svg'; + +const General = () => { + const [businessData, setBusinessData] = useState( + null + ); + const [informationData, setInformationData] = + useState(null); + + const handleBusinessSubmit = (data: any) => { + setBusinessData(data); + }; + + const handleInformationSubmit = (data: any) => { + setInformationData(data); + }; + + return ( +
+
+
+
+ business photo + +
+

+ {businessData?.businessName || '[Business Name]'} +

+ + +
+
+
+ +
+
+ +
+
+
+

Information

+ +
+
+
+
+

Email:

+

{informationData?.email || null}

+
+
+

Phone Number:

+

{informationData?.phoneNumber || null}

+
+
+

BVN:

+

{informationData?.bvn || null}

+
+
+
+
+

Address Line 1:

+

{informationData?.addressLine1 || null}

+
+
+

Address Line 2:

+

{informationData?.addressLine2 || null}

+
+
+
+
+

Country:

+

{informationData?.country || null}

+
+
+

City:

+

{informationData?.city || null}

+
+
+

Postal Code:

+

{informationData?.postalCode || null}

+
+
+
+
+
+
+
+ ); +}; + +export default General; diff --git a/app/(dashboard)/settings/_components/NewPassword.tsx b/app/(dashboard)/settings/_components/NewPassword.tsx new file mode 100644 index 0000000..a628bbd --- /dev/null +++ b/app/(dashboard)/settings/_components/NewPassword.tsx @@ -0,0 +1,58 @@ +import { LockKeyhole } from 'lucide-react'; +import React from 'react'; +import { FieldValues, useForm } from 'react-hook-form'; + +type Props = { + nextPasswordStep: () => void; +}; + +const NewPassword = ({ nextPasswordStep }: Props) => { + const { register, handleSubmit, watch } = useForm(); + + const handleOnSubmit = (data: FieldValues) => { + nextPasswordStep(); + console.log(data); + }; + + const nameValue = watch('confirmNewPassword'); + + return ( +
+
+ +
+
+

+ Create new password +

+

+ Please create a password that's different from your previous + ones. It must be at least 4 digits. +

+
+
+ + + +
+
+ ); +}; + +export default NewPassword; diff --git a/app/(dashboard)/settings/_components/Password.tsx b/app/(dashboard)/settings/_components/Password.tsx new file mode 100644 index 0000000..7718b40 --- /dev/null +++ b/app/(dashboard)/settings/_components/Password.tsx @@ -0,0 +1,42 @@ +import React, { useState } from 'react'; +import PasswordReset from './PasswordReset'; +import ConfirmationCode from './ConfirmationCode'; +import NewPassword from './NewPassword'; +import ResetSuccessful from './ResetSuccessful'; + +const Password = () => { + const [passwordStep, setPasswordStep] = useState(1); + + return ( +
+
+ {passwordStep === 1 && ( + setPasswordStep(2)} /> + )} + {passwordStep === 2 && ( + setPasswordStep(3)} /> + )} + {passwordStep === 3 && ( + setPasswordStep(4)} /> + )} + {passwordStep === 4 && } +
+ + {/* Step Indicator */} +
+ {[1, 2, 3, 4].map((step) => ( +
+ ))} +
+
+ ); +}; + +export default Password; diff --git a/app/(dashboard)/settings/_components/PasswordReset.tsx b/app/(dashboard)/settings/_components/PasswordReset.tsx new file mode 100644 index 0000000..933d8cd --- /dev/null +++ b/app/(dashboard)/settings/_components/PasswordReset.tsx @@ -0,0 +1,59 @@ +import { LockKeyhole } from 'lucide-react'; +import React from 'react'; +import { FieldValues, useForm } from 'react-hook-form'; + +type Props = { + nextPasswordStep: () => void; +}; + +const PasswordReset = ({ nextPasswordStep }: Props) => { + const { register, handleSubmit, watch } = useForm(); + + const handleOnSubmit = (data: FieldValues) => { + nextPasswordStep(); + console.log(data); + }; + + const emailValue = watch('email'); + const passwordValue = watch('password'); + + return ( +
+
+ +
+
+

+ Reset your password +

+

+ Want to reset your password? Please enter your email address and + current password and you'll be sent a 4-digit code. +

+
+
+ + + +
+
+ ); +}; + +export default PasswordReset; diff --git a/app/(dashboard)/settings/_components/Permission.tsx b/app/(dashboard)/settings/_components/Permission.tsx new file mode 100644 index 0000000..94bd67c --- /dev/null +++ b/app/(dashboard)/settings/_components/Permission.tsx @@ -0,0 +1,39 @@ +import FilterBtn from '@/components/DataTable/Filter/Filter'; +import Refresh from '@/components/DataTable/Refresh/Refresh'; +import SearchBar from '@/components/DataTable/Search/Search'; +import React from 'react'; +import { AddStaff } from './AddStaff'; +import Export from '@/components/DataTable/Export/Export'; +import { DataTable } from '@/components/DataTable/DataTable'; +import { permissionColumn, permissionData } from '@/constants/tableData'; + +const Permission = () => { + return ( +
+
+
+ +
+ + +
+
+
+ + +
+
+
+ +
+
+ ); +}; + +export default Permission; diff --git a/app/(dashboard)/settings/_components/ResetSuccessful.tsx b/app/(dashboard)/settings/_components/ResetSuccessful.tsx new file mode 100644 index 0000000..b5ca783 --- /dev/null +++ b/app/(dashboard)/settings/_components/ResetSuccessful.tsx @@ -0,0 +1,33 @@ +import { ShieldCheck } from 'lucide-react'; +import { useRouter } from 'next/navigation'; +import React from 'react'; + +const ResetSuccessful = () => { + const router = useRouter(); + + return ( +
+
+ +
+
+

+ Password reset! +

+

+ Your password has been successfully reset. Click below to log back in. +

+
+ + +
+ ); +}; + +export default ResetSuccessful; diff --git a/app/(dashboard)/settings/_components/SettingsNavbar.tsx b/app/(dashboard)/settings/_components/SettingsNavbar.tsx new file mode 100644 index 0000000..b3543b6 --- /dev/null +++ b/app/(dashboard)/settings/_components/SettingsNavbar.tsx @@ -0,0 +1,56 @@ +import classNames from 'classnames'; +import React from 'react'; + +type Props = { + handleClickSettings: (item: string) => void; + selectedSetting: string; +}; + +const SettingsNavbar = ({ handleClickSettings, selectedSetting }: Props) => { + const settingsNavbar = [ + { + key: 'general', + text: 'General', + }, + { + key: 'staff', + text: 'Staff', + }, + { + key: 'permission', + text: 'Permission', + }, + { + key: 'password', + text: 'Password', + }, + ]; + + return ( +
+
+ {settingsNavbar.map(({ key, text }) => { + const isActive = selectedSetting === key; + + return ( + handleClickSettings(key)} + key={key} + className={classNames( + `hover:bg-[#DEE4FF] py-1 sm:py-2 px-3 sm:px-[18px] rounded-[20px] cursor-pointer relative text-xs`, + { + 'bg-[#DEE4FF] font-semibold text-[#0F172A] active': isActive, + }, + { 'bg-transparent text-[#475569]': !isActive } + )} + > + {text} + + ); + })} +
+
+ ); +}; + +export default SettingsNavbar; diff --git a/app/(dashboard)/settings/_components/Staff.tsx b/app/(dashboard)/settings/_components/Staff.tsx new file mode 100644 index 0000000..576ac51 --- /dev/null +++ b/app/(dashboard)/settings/_components/Staff.tsx @@ -0,0 +1,85 @@ +import { DataTable } from '@/components/DataTable/DataTable'; +import Export from '@/components/DataTable/Export/Export'; +import FilterBtn from '@/components/DataTable/Filter/Filter'; +import Refresh from '@/components/DataTable/Refresh/Refresh'; +import SearchBar from '@/components/DataTable/Search/Search'; +import { staffSettingsColumn, staffSettingsData } from '@/constants/tableData'; +import React from 'react'; +import { AddStaff } from './AddStaff'; + +const Staff = () => { + return ( +
+
+
+
+
+
+

Email:

+

user@paystride.com

+
+
+

Phone Number:

+

+234 201 *** ****

+
+
+

BVN:

+

0000 000 0000

+
+
+
+
+

Address Line 1:

+

+ 14 Idowu Martins Street, Victoria Island +

+
+
+
+ +
+
+

Country:

+

Nigeria

+
+
+

City:

+

Lagos

+
+
+

Postal Code:

+

105102

+
+
+
+
+ +
+
+
+ +
+ + +
+
+
+ + +
+
+
+ +
+
+
+ ); +}; + +export default Staff; diff --git a/app/(dashboard)/settings/_components/ToggleSwitch.tsx b/app/(dashboard)/settings/_components/ToggleSwitch.tsx new file mode 100644 index 0000000..3ed2f9a --- /dev/null +++ b/app/(dashboard)/settings/_components/ToggleSwitch.tsx @@ -0,0 +1,31 @@ +import React, { useState } from 'react'; + +type ToggleProps = { + isOn: boolean; + // onToggle: () => void; +}; + +const ToggleSwitch = ({ isOn }: ToggleProps) => { + const [toggleState, setToggleState] = useState(isOn); + + const handleToggle = () => { + setToggleState(!toggleState); + }; + + return ( +
e.key === 'Enter' && handleToggle()} + > +
+
+ ); +}; + +export default ToggleSwitch; diff --git a/app/(dashboard)/settings/page.tsx b/app/(dashboard)/settings/page.tsx index ceb90fc..663347f 100644 --- a/app/(dashboard)/settings/page.tsx +++ b/app/(dashboard)/settings/page.tsx @@ -1,7 +1,42 @@ -import React from 'react'; +'use client'; +import React, { useState } from 'react'; +import General from './_components/General'; +import Staff from './_components/Staff'; +import Password from './_components/Password'; +import Permission from './_components/Permission'; +import SettingsNavbar from './_components/SettingsNavbar'; const Settings = () => { - return
Settings
; + const [selectedSettings, setSelectedSettings] = useState('general'); + + const handleCickSettings = (item: string) => { + setSelectedSettings(item); + }; + + const stepToRender = (key: string) => { + switch (key) { + case 'general': + return ; + case 'staff': + return ; + case 'permission': + return ; + case 'password': + return ; + default: + break; + } + }; + + return ( +
+ +
{stepToRender(selectedSettings)}
+
+ ); }; export default Settings; diff --git a/components/Checkbox/Checkbox.tsx b/components/Checkbox/Checkbox.tsx new file mode 100644 index 0000000..b48551e --- /dev/null +++ b/components/Checkbox/Checkbox.tsx @@ -0,0 +1,85 @@ +import { Check } from 'lucide-react'; +import React, { useState } from 'react'; + +interface CheckboxProps { + label?: string; + register?: any; + use?: string; +} + +const Checkbox = ({ label, register, use }: CheckboxProps) => { + const [checked, setChecked] = useState(false); + + const handleToggle = () => { + setChecked(!checked); + }; + + return ( + <> + {use === 'table' ? ( +
+ + + +
+ ) : ( +
+ + + + + {label} +
+ )} + + + //
+ // + + // + + // {label} + //
+ ); +}; + +export default Checkbox; diff --git a/components/DataTable/Export/Export.tsx b/components/DataTable/Export/Export.tsx index eb5790d..ea9cf10 100644 --- a/components/DataTable/Export/Export.tsx +++ b/components/DataTable/Export/Export.tsx @@ -1,13 +1,19 @@ import { CloudDownload } from 'lucide-react'; import React from 'react'; -const Export = () => { +type ExportProps = { + iconColor?: string; + btnColor?: React.CSSProperties; +}; + +const Export = ({ iconColor, btnColor }: ExportProps) => { return ( ); diff --git a/components/SuccesModal/SuccesModal.tsx b/components/SuccesModal/SuccesModal.tsx index d98b07b..4647b78 100644 --- a/components/SuccesModal/SuccesModal.tsx +++ b/components/SuccesModal/SuccesModal.tsx @@ -2,14 +2,21 @@ import React from 'react'; import successIcon from '@/app/assets/successIcon.svg'; import Image from 'next/image'; import { AlertDialogCancel } from '@/components/ui/alert-dialog'; +import { SheetClose } from '../ui/sheet'; type SuccessModalProp = { title?: string; close: string; message: string; + closeContent?: string; }; -const SuccesModal = ({ title, message, close }: SuccessModalProp) => { +const SuccesModal = ({ + title, + message, + close, + closeContent, +}: SuccessModalProp) => { return (
@@ -22,12 +29,18 @@ const SuccesModal = ({ title, message, close }: SuccessModalProp) => { height={120} className="w-24 sm:w-[120px]" /> - + {message} - - {close} - + {closeContent === 'sheet' ? ( + + {close} + + ) : ( + + {close} + + )}
diff --git a/components/ui/alert-dialog.tsx b/components/ui/alert-dialog.tsx index 5b558e4..b5c4585 100644 --- a/components/ui/alert-dialog.tsx +++ b/components/ui/alert-dialog.tsx @@ -36,7 +36,7 @@ const AlertDialogContent = React.forwardRef< , + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +SheetOverlay.displayName = SheetPrimitive.Overlay.displayName; + +const sheetVariants = cva( + 'fixed z-50 gap-4 bg-background rounded-[20px] p-4 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500 data-[state=open]:animate-in data-[state=closed]:animate-out', + { + variants: { + side: { + top: 'inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top', + bottom: + 'inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom', + left: 'inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm', + right: + 'inset-y-0 right-0 h-full w-5/6 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm', + center: + 'grid min-w-[88%] sm:min-w-[384px] translate-x-[-50%] md:translate-x-[calc(-50%+21rem)] translate-y-[-50%] top-[50%] left-[50%] md:left-[calc(50%-14rem)] rounded-[20px] !duration-0 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0', + }, + }, + defaultVariants: { + side: 'right', + }, + } +); + +interface SheetContentProps + extends React.ComponentPropsWithoutRef, + VariantProps {} + +const SheetContent = React.forwardRef< + React.ElementRef, + SheetContentProps +>(({ side = 'right', className, children, ...props }, ref) => ( + + + + + + Close + + {children} + + +)); +SheetContent.displayName = SheetPrimitive.Content.displayName; + +const SheetHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+); +SheetHeader.displayName = 'SheetHeader'; + +const SheetFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+); +SheetFooter.displayName = 'SheetFooter'; + +const SheetTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +SheetTitle.displayName = SheetPrimitive.Title.displayName; + +const SheetDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +SheetDescription.displayName = SheetPrimitive.Description.displayName; + +export { + Sheet, + SheetPortal, + SheetOverlay, + SheetTrigger, + SheetClose, + SheetContent, + SheetHeader, + SheetFooter, + SheetTitle, + SheetDescription, +}; diff --git a/constants/tableData.tsx b/constants/tableData.tsx index 4f2b399..0d9189d 100644 --- a/constants/tableData.tsx +++ b/constants/tableData.tsx @@ -1,6 +1,6 @@ 'use client'; import { ColumnDef } from '@tanstack/react-table'; -import { ChevronsUpDown } from 'lucide-react'; +import { Check, ChevronsUpDown, Pencil, Trash2 } from 'lucide-react'; import Corey from '@/app/(dashboard)/assets/corey.svg'; import Kaiya from '@/app/(dashboard)/assets/kaiya.jpg'; import SalesName from '@/components/DataTable/SalesName/SalesName'; @@ -8,6 +8,9 @@ import DateTime from '@/components/DataTable/DateTime/DateTime'; import TableStatus from '@/components/DataTable/TableSatus/TableStatus'; import TableMethod from '@/components/DataTable/TableMethod/TableMethod'; import EditModal from '@/app/(dashboard)/payment-point/_component/EditModal'; +import ToggleSwitch from '@/app/(dashboard)/settings/_components/ToggleSwitch'; +import Checkbox from '@/components/Checkbox/Checkbox'; +import { useState } from 'react'; type SalesData = { name: string; @@ -49,6 +52,21 @@ type SettlementsData = { amount: string; }; +type StaffSettingsData = { + name: string; + image: string | any; + phone: string; + role: string; + department: string; + action: boolean; +}; + +type PermissionData = { + name: string; + image: string | any; + phone: string; +}; + export const salesData: SalesData[] = [ { name: 'Corey Carder', @@ -515,3 +533,254 @@ export const settlementsColumn: ColumnDef[] = [ header: () =>
Amount
, }, ]; + +export const staffSettingsData: StaffSettingsData[] = [ + { + name: 'Corey Carder', + image: Corey, + phone: '08123231212', + role: 'Cashier', + department: 'Sales', + action: true, + }, + { + name: 'Kaiya Batista', + image: Kaiya, + phone: '08122222222', + role: 'Cashier', + department: 'Sales', + action: true, + }, + { + name: 'Charlie Lucatero', + image: Corey, + phone: '08178231213', + role: 'Sales Manager', + department: 'Sales', + action: true, + }, + { + name: 'Karla Bosco', + image: Corey, + phone: '9225433854', + role: 'Operation Manager', + department: 'Sales', + action: false, + }, + { + name: 'Kiera Herwitz', + image: Corey, + phone: '07011111111', + role: 'Sales Manager', + department: 'Sales', + action: false, + }, + { + name: 'Brandon Goyette', + image: Corey, + phone: '8254502917', + role: 'Operation Managerr', + department: 'Sales', + action: false, + }, +]; + +export const staffSettingsColumn: ColumnDef[] = [ + { + accessorKey: 'name', + header: ({ column }) => ( +
+ +
+ ), + cell: ({ row }) => ( + + ), + }, + { + accessorKey: 'role', + header: () => ( +
Role
+ ), + }, + { + accessorKey: 'department', + header: () => ( +
+ Department +
+ ), + cell: ({ row }) => ( +
{row.original.department}
+ ), + }, + { + accessorKey: 'action', + header: () => ( +
+ Action +
+ ), + cell: ({ row }) => ( +
+ +
+ ), + }, + { + id: 'edit', + header: () => ( +
+ Edit +
+ ), + cell: ({}) => ( +
+ +
+ ), + }, + { + id: 'removeStaff', + header: () => ( +
+ Remove Staff +
+ ), + cell: ({}) => ( +
+ +
+ ), + }, +]; + +export const permissionData: PermissionData[] = [ + { + name: 'Corey Carder', + image: Corey, + phone: '08123231212', + }, + { + name: 'Kaiya Batista', + image: Kaiya, + phone: '08122222222', + }, + { + name: 'Charlie Lucatero', + image: Corey, + phone: '08178231213', + }, + { + name: 'Karla Bosco', + image: Corey, + phone: '9225433854', + }, + { + name: 'Kiera Herwitz', + image: Corey, + phone: '07011111111', + }, + { + name: 'Brandon Goyette', + image: Corey, + phone: '8254502917', + }, +]; + +export const permissionColumn: ColumnDef[] = [ + { + accessorKey: 'name', + header: ({ column }) => ( +
+ +
+ ), + cell: ({ row }) => ( + + ), + }, + { + id: 'dashboard', + header: () => ( +
+ Dashboard +
+ ), + cell: ({}) => ( +
+ +
+ ), + }, + { + id: 'transaction', + header: () => ( +
+ Transaction +
+ ), + cell: ({}) => ( +
+ +
+ ), + }, + { + id: 'payPoint', + header: () => ( +
+ PayPoint +
+ ), + cell: ({}) => ( +
+ +
+ ), + }, + { + id: 'settlement', + header: () => ( +
+ Settlement +
+ ), + cell: ({}) => ( +
+ +
+ ), + }, + { + id: 'settings', + header: () => ( +
+ Settings +
+ ), + cell: ({}) => ( +
+ +
+ ), + }, +]; diff --git a/package-lock.json b/package-lock.json index 0c616e6..4324d3d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,11 +11,13 @@ "@hookform/resolvers": "^3.9.1", "@radix-ui/react-accordion": "^1.2.1", "@radix-ui/react-alert-dialog": "^1.1.3", + "@radix-ui/react-dialog": "^1.1.4", "@radix-ui/react-popover": "^1.1.3", "@radix-ui/react-slot": "^1.1.1", "@tanstack/react-table": "^8.20.5", "@typescript-eslint/eslint-plugin": "^8.16.0", "class-variance-authority": "^0.7.1", + "classnames": "^2.5.1", "clsx": "^2.1.1", "date-fns": "^3.6.0", "lucide-react": "^0.468.0", @@ -596,6 +598,64 @@ } } }, + "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-dialog": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.3.tgz", + "integrity": "sha512-ujGvqQNkZ0J7caQyl8XuZRj2/TIrYcOGwqz5TeD1OMcCdfBuEMP0D12ve+8J5F9XuNUth3FAKFWo/wt0E/GJrQ==", + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.2", + "@radix-ui/react-focus-guards": "1.1.1", + "@radix-ui/react-focus-scope": "1.1.1", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-portal": "1.1.3", + "@radix-ui/react-presence": "1.1.2", + "@radix-ui/react-primitive": "2.0.1", + "@radix-ui/react-slot": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.1.0", + "aria-hidden": "^1.1.1", + "react-remove-scroll": "2.6.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-presence": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.2.tgz", + "integrity": "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-primitive": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.1.tgz", @@ -781,14 +841,14 @@ } }, "node_modules/@radix-ui/react-dialog": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.3.tgz", - "integrity": "sha512-ujGvqQNkZ0J7caQyl8XuZRj2/TIrYcOGwqz5TeD1OMcCdfBuEMP0D12ve+8J5F9XuNUth3FAKFWo/wt0E/GJrQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.4.tgz", + "integrity": "sha512-Ur7EV1IwQGCyaAuyDRiOLA5JIUZxELJljF+MbM/2NC0BYwfuRrbpS30BiQBJrVruscgUkieKkqXYDOoByaxIoA==", "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.3", "@radix-ui/react-focus-guards": "1.1.1", "@radix-ui/react-focus-scope": "1.1.1", "@radix-ui/react-id": "1.1.0", @@ -798,7 +858,7 @@ "@radix-ui/react-slot": "1.1.1", "@radix-ui/react-use-controllable-state": "1.1.0", "aria-hidden": "^1.1.1", - "react-remove-scroll": "2.6.0" + "react-remove-scroll": "^2.6.1" }, "peerDependencies": { "@types/react": "*", @@ -834,6 +894,32 @@ } } }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.3.tgz", + "integrity": "sha512-onrWn/72lQoEucDmJnr8uczSNTujT0vJnA/X5+3AkChVPowr8n1yvIKIabhWyMQeMvvmdpsvcyDqx3X1LEXCPg==", + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-primitive": "2.0.1", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-escape-keydown": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-presence": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.2.tgz", @@ -879,6 +965,30 @@ } } }, + "node_modules/@radix-ui/react-dialog/node_modules/react-remove-scroll": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.6.2.tgz", + "integrity": "sha512-KmONPx5fnlXYJQqC62Q+lwIeAk64ws/cUw6omIumRzMRPqgnYqhSSti99nbj0Ry13bv7dF+BKn7NB+OqkdZGTw==", + "dependencies": { + "react-remove-scroll-bar": "^2.3.7", + "react-style-singleton": "^2.2.1", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.3", + "use-sidecar": "^1.1.2" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-direction": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.0.tgz", @@ -2306,6 +2416,11 @@ "url": "https://polar.sh/cva" } }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==" + }, "node_modules/cli-cursor": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", @@ -3957,14 +4072,6 @@ "node": ">= 0.4" } }, - "node_modules/invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "dependencies": { - "loose-envify": "^1.0.0" - } - }, "node_modules/is-array-buffer": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", @@ -5809,19 +5916,19 @@ } }, "node_modules/react-remove-scroll-bar": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.6.tgz", - "integrity": "sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==", + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", + "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", "dependencies": { - "react-style-singleton": "^2.2.1", + "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "engines": { "node": ">=10" }, "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@types/react": { @@ -5830,20 +5937,19 @@ } }, "node_modules/react-style-singleton": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz", - "integrity": "sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", + "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", "dependencies": { "get-nonce": "^1.0.0", - "invariant": "^2.2.4", "tslib": "^2.0.0" }, "engines": { "node": ">=10" }, "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { @@ -6879,9 +6985,9 @@ } }, "node_modules/use-callback-ref": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.2.tgz", - "integrity": "sha512-elOQwe6Q8gqZgDA8mrh44qRTQqpIHDcZ3hXTLjBe1i4ph8XpNJnO+aQf3NaG+lriLopI4HMx9VjQLfPQ6vhnoA==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", + "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", "dependencies": { "tslib": "^2.0.0" }, @@ -6889,8 +6995,8 @@ "node": ">=10" }, "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { diff --git a/package.json b/package.json index aca3549..362586d 100644 --- a/package.json +++ b/package.json @@ -15,11 +15,13 @@ "@hookform/resolvers": "^3.9.1", "@radix-ui/react-accordion": "^1.2.1", "@radix-ui/react-alert-dialog": "^1.1.3", + "@radix-ui/react-dialog": "^1.1.4", "@radix-ui/react-popover": "^1.1.3", "@radix-ui/react-slot": "^1.1.1", "@tanstack/react-table": "^8.20.5", "@typescript-eslint/eslint-plugin": "^8.16.0", "class-variance-authority": "^0.7.1", + "classnames": "^2.5.1", "clsx": "^2.1.1", "date-fns": "^3.6.0", "lucide-react": "^0.468.0", diff --git a/types/types.ts b/types/types.ts index e69de29..7c0eb38 100644 --- a/types/types.ts +++ b/types/types.ts @@ -0,0 +1,17 @@ +export type BusinessFormData = { + businessPhoto: string; + businessName: string; + memberSince: Date; + employeeNo: string; +}; + +export type InformationFormData = { + email: string; + phoneNumber: string; + bvn: string; + addressLine1: string; + addressLine2: string; + country: string; + city: string; + postalCode: string; +};