Merged in feature-payment-block-corporate-update (pull request #9)

Blocked Corporate user update through payment records screen

Approved-by: Tiago Ribeiro
This commit is contained in:
João Ramos
2023-12-26 20:25:46 +00:00
committed by Tiago Ribeiro
3 changed files with 171 additions and 55 deletions

View File

@@ -6,11 +6,15 @@ interface Props {
isChecked: boolean; isChecked: boolean;
onChange: (isChecked: boolean) => void; onChange: (isChecked: boolean) => void;
children: ReactNode; children: ReactNode;
disabled?: boolean;
} }
export default function Checkbox({isChecked, onChange, children}: Props) { export default function Checkbox({isChecked, onChange, children, disabled}: Props) {
return ( return (
<div className="flex gap-3 items-center text-mti-gray-dim text-sm cursor-pointer" onClick={() => onChange(!isChecked)}> <div className="flex gap-3 items-center text-mti-gray-dim text-sm cursor-pointer" onClick={() => {
if(disabled) return;
onChange(!isChecked);
}}>
<input type="checkbox" className="hidden" /> <input type="checkbox" className="hidden" />
<div <div
className={clsx( className={clsx(

View File

@@ -36,9 +36,30 @@ interface Props {
onViewStudents?: () => void; onViewStudents?: () => void;
onViewTeachers?: () => void; onViewTeachers?: () => void;
onViewCorporate?: () => void; onViewCorporate?: () => void;
disabled?: boolean;
} }
const UserCard = ({user, loggedInUser, onClose, onViewStudents, onViewTeachers, onViewCorporate}: Props) => { const USER_STATUS_OPTIONS = [
{
value: 'active',
label: 'Active',
}, {
value: 'disabled',
label: 'Disabled',
}, {
value: 'paymentDue',
label: 'Payment Due',
}
];
const USER_TYPE_OPTIONS = Object.keys(USER_TYPE_LABELS).map((type) => ({
value: type,
label: USER_TYPE_LABELS[type as keyof typeof USER_TYPE_LABELS]
}));
const CURRENCIES_OPTIONS = CURRENCIES.map(({ label, currency}) => ({ value: currency, label }));
const UserCard = ({user, loggedInUser, onClose, onViewStudents, onViewTeachers, onViewCorporate, disabled = false}: Props) => {
const [expiryDate, setExpiryDate] = useState<Date | null | undefined>(user.subscriptionExpirationDate); const [expiryDate, setExpiryDate] = useState<Date | null | undefined>(user.subscriptionExpirationDate);
const [type, setType] = useState(user.type); const [type, setType] = useState(user.type);
const [status, setStatus] = useState(user.status); const [status, setStatus] = useState(user.status);
@@ -154,6 +175,7 @@ const UserCard = ({user, loggedInUser, onClose, onViewStudents, onViewTeachers,
placeholder="Enter corporate name" placeholder="Enter corporate name"
defaultValue={companyName} defaultValue={companyName}
required required
disabled={disabled}
/> />
<Input <Input
label="Commercial Registration" label="Commercial Registration"
@@ -163,6 +185,7 @@ const UserCard = ({user, loggedInUser, onClose, onViewStudents, onViewTeachers,
placeholder="Enter commercial registration" placeholder="Enter commercial registration"
defaultValue={commercialRegistration} defaultValue={commercialRegistration}
required required
disabled={disabled}
/> />
</div> </div>
<Divider className="w-full !m-0" /> <Divider className="w-full !m-0" />
@@ -178,6 +201,7 @@ const UserCard = ({user, loggedInUser, onClose, onViewStudents, onViewTeachers,
onChange={setCompanyName} onChange={setCompanyName}
placeholder="Enter corporate name" placeholder="Enter corporate name"
defaultValue={companyName} defaultValue={companyName}
disabled={disabled}
/> />
<Input <Input
label="Number of Users" label="Number of Users"
@@ -186,6 +210,7 @@ const UserCard = ({user, loggedInUser, onClose, onViewStudents, onViewTeachers,
onChange={(e) => setUserAmount(e ? parseInt(e) : undefined)} onChange={(e) => setUserAmount(e ? parseInt(e) : undefined)}
placeholder="Enter number of users" placeholder="Enter number of users"
defaultValue={userAmount} defaultValue={userAmount}
disabled={disabled}
/> />
<Input <Input
label="Monthly Duration" label="Monthly Duration"
@@ -194,6 +219,7 @@ const UserCard = ({user, loggedInUser, onClose, onViewStudents, onViewTeachers,
onChange={(e) => setMonthlyDuration(e ? parseInt(e) : undefined)} onChange={(e) => setMonthlyDuration(e ? parseInt(e) : undefined)}
placeholder="Enter monthly duration" placeholder="Enter monthly duration"
defaultValue={monthlyDuration} defaultValue={monthlyDuration}
disabled={disabled}
/> />
<div className="flex flex-col gap-3 w-full lg:col-span-2"> <div className="flex flex-col gap-3 w-full lg:col-span-2">
<label className="font-normal text-base text-mti-gray-dim">Pricing</label> <label className="font-normal text-base text-mti-gray-dim">Pricing</label>
@@ -204,17 +230,31 @@ const UserCard = ({user, loggedInUser, onClose, onViewStudents, onViewTeachers,
type="number" type="number"
defaultValue={paymentValue || 0} defaultValue={paymentValue || 0}
className="col-span-3" className="col-span-3"
disabled={disabled}
/>
<Select
className="px-4 py-4 w-full text-sm font-normal placeholder:text-mti-gray-cool disabled:bg-mti-gray-platinum/40 disabled:text-mti-gray-dim disabled:cursor-not-allowed bg-white rounded-full border border-mti-gray-platinum focus:outline-none"
options={CURRENCIES_OPTIONS}
value={CURRENCIES_OPTIONS.find((c) => c.value === paymentCurrency)}
onChange={(value) => setPaymentCurrency(value?.value)}
styles={{
control: (styles) => ({
...styles,
paddingLeft: "4px",
border: "none",
outline: "none",
":focus": {
outline: "none",
},
}),
option: (styles, state) => ({
...styles,
backgroundColor: state.isFocused ? "#D5D9F0" : state.isSelected ? "#7872BF" : "white",
color: state.isFocused ? "black" : styles.color,
}),
}}
isDisabled={disabled}
/> />
<select
defaultValue={paymentCurrency}
onChange={(e) => setPaymentCurrency(e.target.value)}
className="p-6 col-span-2 w-full min-h-[70px] flex justify-center text-sm font-normal rounded-full border focus:outline-none cursor-pointer bg-white">
{CURRENCIES.map(({label, currency}) => (
<option value={currency} key={currency}>
{label}
</option>
))}
</select>
</div> </div>
</div> </div>
</div> </div>
@@ -249,6 +289,7 @@ const UserCard = ({user, loggedInUser, onClose, onViewStudents, onViewTeachers,
color: state.isFocused ? "black" : styles.color, color: state.isFocused ? "black" : styles.color,
}), }),
}} }}
isDisabled={disabled}
/> />
)} )}
</div> </div>
@@ -262,6 +303,7 @@ const UserCard = ({user, loggedInUser, onClose, onViewStudents, onViewTeachers,
type="number" type="number"
defaultValue={commissionValue || 0} defaultValue={commissionValue || 0}
className="col-span-3" className="col-span-3"
disabled={disabled}
/> />
</> </>
) : ( ) : (
@@ -316,7 +358,8 @@ const UserCard = ({user, loggedInUser, onClose, onViewStudents, onViewTeachers,
<label className="font-normal text-base text-mti-gray-dim">Employment Status</label> <label className="font-normal text-base text-mti-gray-dim">Employment Status</label>
<RadioGroup <RadioGroup
value={user.demographicInformation?.employment} value={user.demographicInformation?.employment}
className="grid grid-cols-2 items-center gap-4 place-items-center"> className="grid grid-cols-2 items-center gap-4 place-items-center"
disabled={disabled}>
{EMPLOYMENT_STATUS.map(({status, label}) => ( {EMPLOYMENT_STATUS.map(({status, label}) => (
<RadioGroup.Option value={status} key={status}> <RadioGroup.Option value={status} key={status}>
{({checked}) => ( {({checked}) => (
@@ -351,7 +394,11 @@ const UserCard = ({user, loggedInUser, onClose, onViewStudents, onViewTeachers,
<div className="flex flex-col gap-8 w-full"> <div className="flex flex-col gap-8 w-full">
<div className="relative flex flex-col gap-3 w-full"> <div className="relative flex flex-col gap-3 w-full">
<label className="font-normal text-base text-mti-gray-dim">Gender</label> <label className="font-normal text-base text-mti-gray-dim">Gender</label>
<RadioGroup value={user.demographicInformation?.gender} className="flex flex-row gap-4 justify-between"> <RadioGroup
value={user.demographicInformation?.gender}
className="flex flex-row gap-4 justify-between"
disabled={disabled}
>
<RadioGroup.Option value="male"> <RadioGroup.Option value="male">
{({checked}) => ( {({checked}) => (
<span <span
@@ -401,7 +448,9 @@ const UserCard = ({user, loggedInUser, onClose, onViewStudents, onViewTeachers,
<label className="font-normal text-base text-mti-gray-dim">Expiry Date</label> <label className="font-normal text-base text-mti-gray-dim">Expiry Date</label>
<Checkbox <Checkbox
isChecked={!!expiryDate} isChecked={!!expiryDate}
onChange={(checked) => setExpiryDate(checked ? user.subscriptionExpirationDate || new Date() : null)}> onChange={(checked) => setExpiryDate(checked ? user.subscriptionExpirationDate || new Date() : null)}
disabled={disabled}
>
Enabled Enabled
</Checkbox> </Checkbox>
</div> </div>
@@ -434,6 +483,7 @@ const UserCard = ({user, loggedInUser, onClose, onViewStudents, onViewTeachers,
dateFormat="dd/MM/yyyy" dateFormat="dd/MM/yyyy"
selected={moment(expiryDate).toDate()} selected={moment(expiryDate).toDate()}
onChange={(date) => setExpiryDate(date)} onChange={(date) => setExpiryDate(date)}
disabled={disabled}
/> />
)} )}
</div> </div>
@@ -445,27 +495,55 @@ const UserCard = ({user, loggedInUser, onClose, onViewStudents, onViewTeachers,
<div className="flex flex-col md:flex-row gap-8 w-full"> <div className="flex flex-col md:flex-row gap-8 w-full">
<div className="flex flex-col gap-3 w-full"> <div className="flex flex-col gap-3 w-full">
<label className="font-normal text-base text-mti-gray-dim">Status</label> <label className="font-normal text-base text-mti-gray-dim">Status</label>
<select <Select
defaultValue={user.status} className="px-4 py-4 w-full text-sm font-normal placeholder:text-mti-gray-cool disabled:bg-mti-gray-platinum/40 disabled:text-mti-gray-dim disabled:cursor-not-allowed bg-white rounded-full border border-mti-gray-platinum focus:outline-none"
onChange={(e) => setStatus(e.target.value as typeof user.status)} options={USER_STATUS_OPTIONS}
className="p-6 w-full min-h-[70px] flex justify-center text-sm font-normal rounded-full border focus:outline-none cursor-pointer bg-white"> value={USER_STATUS_OPTIONS.find((o) => o.value === status)}
<option value="active">Active</option> onChange={(value) => setStatus(value?.value as typeof user.status)}
<option value="disabled">Disabled</option> styles={{
<option value="paymentDue">Payment Due</option> control: (styles) => ({
</select> ...styles,
paddingLeft: "4px",
border: "none",
outline: "none",
":focus": {
outline: "none",
},
}),
option: (styles, state) => ({
...styles,
backgroundColor: state.isFocused ? "#D5D9F0" : state.isSelected ? "#7872BF" : "white",
color: state.isFocused ? "black" : styles.color,
}),
}}
isDisabled={disabled}
/>
</div> </div>
<div className="flex flex-col gap-3 w-full"> <div className="flex flex-col gap-3 w-full">
<label className="font-normal text-base text-mti-gray-dim">Type</label> <label className="font-normal text-base text-mti-gray-dim">Type</label>
<select <Select
defaultValue={user.type} className="px-4 py-4 w-full text-sm font-normal placeholder:text-mti-gray-cool disabled:bg-mti-gray-platinum/40 disabled:text-mti-gray-dim disabled:cursor-not-allowed bg-white rounded-full border border-mti-gray-platinum focus:outline-none"
onChange={(e) => setType(e.target.value as typeof user.type)} options={USER_TYPE_OPTIONS}
className="p-6 w-full min-h-[70px] flex justify-center text-sm font-normal rounded-full border focus:outline-none cursor-pointer bg-white"> value={USER_TYPE_OPTIONS.find((o) => o.value === type)}
{Object.keys(USER_TYPE_LABELS).map((type) => ( onChange={(value) => setType(value?.value as typeof user.type)}
<option key={type} value={type}> styles={{
{USER_TYPE_LABELS[type as keyof typeof USER_TYPE_LABELS]} control: (styles) => ({
</option> ...styles,
))} paddingLeft: "4px",
</select> border: "none",
outline: "none",
":focus": {
outline: "none",
},
}),
option: (styles, state) => ({
...styles,
backgroundColor: state.isFocused ? "#D5D9F0" : state.isSelected ? "#7872BF" : "white",
color: state.isFocused ? "black" : styles.color,
}),
}}
isDisabled={disabled}
/>
</div> </div>
</div> </div>
</> </>
@@ -494,7 +572,7 @@ const UserCard = ({user, loggedInUser, onClose, onViewStudents, onViewTeachers,
<Button className="w-full max-w-[200px]" variant="outline" onClick={onClose}> <Button className="w-full max-w-[200px]" variant="outline" onClick={onClose}>
Close Close
</Button> </Button>
<Button onClick={updateUser} className="w-full max-w-[200px]"> <Button disabled={disabled} onClick={updateUser} className="w-full max-w-[200px]">
Update Update
</Button> </Button>
</div> </div>

View File

@@ -237,7 +237,8 @@ const PaymentCreator = ({onClose, reload, showComission = false}: {onClose: () =
}; };
export default function PaymentRecord() { export default function PaymentRecord() {
const [selectedUser, setSelectedUser] = useState<User>(); const [selectedCorporateUser, setSelectedCorporateUser] = useState<User>();
const [selectedAgentUser, setSelectedAgentUser] = useState<User>();
const [isCreatingPayment, setIsCreatingPayment] = useState(false); const [isCreatingPayment, setIsCreatingPayment] = useState(false);
const [filters, setFilters] = useState<{filter: (p: Payment) => boolean; id: string}[]>([]); const [filters, setFilters] = useState<{filter: (p: Payment) => boolean; id: string}[]>([]);
const [displayPayments, setDisplayPayments] = useState<Payment[]>([]); const [displayPayments, setDisplayPayments] = useState<Payment[]>([]);
@@ -437,7 +438,7 @@ export default function PaymentRecord() {
className={clsx( className={clsx(
"underline text-mti-purple-light hover:text-mti-purple-dark transition ease-in-out duration-300 cursor-pointer", "underline text-mti-purple-light hover:text-mti-purple-dark transition ease-in-out duration-300 cursor-pointer",
)} )}
onClick={() => setSelectedUser(user)}> onClick={() => setSelectedCorporateUser(user)}>
{user?.corporateInformation.companyInformation.name || user?.name} {user?.corporateInformation.companyInformation.name || user?.name}
</div> </div>
); );
@@ -463,7 +464,7 @@ export default function PaymentRecord() {
cell: (info) => ( cell: (info) => (
<div <div
className={clsx("underline text-mti-purple-light hover:text-mti-purple-dark transition ease-in-out duration-300 cursor-pointer")} className={clsx("underline text-mti-purple-light hover:text-mti-purple-dark transition ease-in-out duration-300 cursor-pointer")}
onClick={() => setSelectedUser(users.find((x) => x.id === info.row.original.agent))}> onClick={() => setSelectedAgentUser(users.find((x) => x.id === info.row.original.agent))}>
{(users.find((x) => x.id === info.row.original.agent) as AgentUser)?.name} {(users.find((x) => x.id === info.row.original.agent) as AgentUser)?.name}
</div> </div>
), ),
@@ -517,6 +518,55 @@ export default function PaymentRecord() {
getCoreRowModel: getCoreRowModel(), getCoreRowModel: getCoreRowModel(),
}); });
const getUserModal = () => {
if(user) {
if(selectedCorporateUser) {
return (
<Modal isOpen={!!selectedCorporateUser} onClose={() => setSelectedCorporateUser(undefined)}>
<>
{selectedCorporateUser && (
<div className="w-full flex flex-col gap-8">
<UserCard
loggedInUser={user}
onClose={(shouldReload) => {
setSelectedCorporateUser(undefined);
if (shouldReload) reload();
}}
user={selectedCorporateUser}
disabled
/>
</div>
)}
</>
</Modal>
);
}
if(selectedAgentUser) {
return (
<Modal isOpen={!!selectedAgentUser} onClose={() => setSelectedAgentUser(undefined)}>
<>
{selectedAgentUser && (
<div className="w-full flex flex-col gap-8">
<UserCard
loggedInUser={user}
onClose={(shouldReload) => {
setSelectedAgentUser(undefined);
if (shouldReload) reload();
}}
user={selectedAgentUser}
/>
</div>
)}
</>
</Modal>
);
}
}
return null;
}
return ( return (
<> <>
<Head> <Head>
@@ -531,23 +581,7 @@ export default function PaymentRecord() {
<ToastContainer /> <ToastContainer />
{user && ( {user && (
<Layout user={user} className="gap-6"> <Layout user={user} className="gap-6">
<Modal isOpen={!!selectedUser} onClose={() => setSelectedUser(undefined)}> {getUserModal()}
<>
{selectedUser && (
<div className="w-full flex flex-col gap-8">
<UserCard
loggedInUser={user}
onClose={(shouldReload) => {
setSelectedUser(undefined);
if (shouldReload) reload();
}}
user={selectedUser}
/>
</div>
)}
</>
</Modal>
<Modal isOpen={isCreatingPayment} onClose={() => setIsCreatingPayment(false)}> <Modal isOpen={isCreatingPayment} onClose={() => setIsCreatingPayment(false)}>
<PaymentCreator <PaymentCreator
onClose={() => setIsCreatingPayment(false)} onClose={() => setIsCreatingPayment(false)}