update transaction detail

This commit is contained in:
wayanrivan
2025-04-28 15:51:05 +07:00
parent 99687023a3
commit 1f1679c185
4 changed files with 166 additions and 162 deletions

View File

@ -1,95 +1,50 @@
import { useState, useContext, useEffect, useCallback, MouseEvent } from 'react';
import { useState, useContext, useCallback, MouseEvent } from 'react';
import { AccountUserProfileContext } from '../hooks';
import { toast } from 'sonner';
import { useAuthContext } from '@/auth';
import { KeenIcon } from '@/components';
import clsx from 'clsx';
type PasswordType = 'password' | 'retype_password' | 'current_password';
type PasswordType = 'password';
const PinCode = () => {
const { setPassword } = useContext(AccountUserProfileContext);
const { setPincode } = useContext(AccountUserProfileContext);
const { getUser } = useAuthContext();
const [newPassword, setNewPassword] = useState('');
const [currentPassword, setCurrentPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
const [newPincode, setNewPincode] = useState('');
const [isSubmitting, setIsSubmitting] = useState(false);
const [messagePassword, setMessagePassword] = useState(true);
const [showPassword, setShowPassword] = useState({
current_password: false,
password: false,
retype_password: false
});
const [passwordErrors, setPasswordErrors] = useState<string[]>([]);
const validatePassword = (password: string) => {
const hasSpecialChar = /[!@#$%^&*(),.?":{}|<>]/.test(password);
const hasCapitalLetter = /[A-Z]/.test(password);
const hasNumber = /[0-9]/.test(password);
return hasSpecialChar && hasCapitalLetter && hasNumber;
};
useEffect(() => {
const passwordsMatch = newPassword === confirmPassword;
const isPasswordValid = validatePassword(newPassword);
setMessagePassword(passwordsMatch && isPasswordValid);
const errors: string[] = [];
if (newPassword) {
if (!/[A-Z]/.test(newPassword)) {
errors.push('Password must contain at least one capital letter');
}
if (!/[0-9]/.test(newPassword)) {
errors.push('Password must contain at least one number');
}
if (!/[!@#$%^&*(),.?":{}|<>]/.test(newPassword)) {
errors.push('Password must contain at least one special character');
}
if (newPassword !== confirmPassword) {
errors.push('Passwords do not match');
}
}
setPasswordErrors(errors);
}, [newPassword, confirmPassword]);
const handleResetPassword = useCallback(async () => {
const user: any = await getUser();
if (!messagePassword) {
toast.error('Passwords do not match!');
return;
}
setIsSubmitting(true);
try {
await setPassword({
current_password: currentPassword,
password: newPassword,
retype_password: confirmPassword,
username: user.data.username ?? ''
await setPincode({
newpincode: newPincode,
});
setNewPassword('');
setConfirmPassword('');
setCurrentPassword('');
setNewPincode('');
toast.success('Pin code updated successfully');
} catch (error: any) {
const errorMessage =
error?.response?.data?.message ||
error?.message ||
'An error occurred while resetting the password.';
'An error occurred while updating the pin code.';
toast.error(errorMessage);
} finally {
setIsSubmitting(false);
}
}, [currentPassword, newPassword, newPassword, messagePassword, getUser]);
}, [newPincode, setPincode, getUser]);
const isButtonDisabled = isSubmitting || !newPassword || !confirmPassword || !messagePassword;
const isButtonDisabled = isSubmitting || !newPincode;
const togglePassword = useCallback((event: MouseEvent<HTMLButtonElement>, key: string) => {
const togglePassword = useCallback((event: MouseEvent<HTMLButtonElement>, key: PasswordType) => {
event.preventDefault();
setShowPassword((prev) => ({ ...prev, [key]: !prev[key as PasswordType] }));
setShowPassword((prev) => ({ ...prev, [key]: !prev[key] }));
}, []);
return (
@ -99,89 +54,28 @@ const PinCode = () => {
</div>
<div className="card-body grid gap-5">
<div className="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
<label className="form-label max-w-56">Current Pin Code</label>
<div className="input">
<label className="form-label max-w-56">New Pin Code</label>
<div className="input relative">
<input
type={showPassword.current_password ? 'text' : 'password'}
className="form-control"
placeholder="Current Pin Code"
value={currentPassword}
onChange={(e) => setCurrentPassword(e.target.value)}
disabled={isSubmitting}
/>
<button className="btn btn-icon" onClick={(e) => togglePassword(e, 'current_password')}>
<KeenIcon
icon="eye"
className={clsx('text-gray-500', { hidden: showPassword.current_password })}
/>
<KeenIcon
icon="eye-slash"
className={clsx('text-gray-500', { hidden: !showPassword.current_password })}
/>
</button>
</div>
</div>
<div className="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
<label className="form-label max-w-56">Pin Code</label>
<div className="input">
<input
className="form-control"
className="form-control pr-10" // untuk kasih jarak buat tombol mata
placeholder="New Pin Code"
value={newPassword}
onChange={(e) => setNewPassword(e.target.value)}
value={newPincode}
onChange={(e) => setNewPincode(e.target.value)} // ✅ fix
disabled={isSubmitting}
type={showPassword.password ? 'text' : 'password'}
/>
<button className="btn btn-icon" onClick={(e) => togglePassword(e, 'password')}>
<button
className="btn btn-icon absolute right-2 top-1/2 transform -translate-y-1/2"
onClick={(e) => togglePassword(e, 'password')}
type="button"
>
<KeenIcon
icon="eye"
className={clsx('text-gray-500', { hidden: showPassword.password })}
/>
<KeenIcon
icon="eye-slash"
className={clsx('text-gray-500', { hidden: !showPassword.password })}
icon={showPassword.password ? 'eye-slash' : 'eye'}
className="text-gray-500"
/>
</button>
</div>
</div>
<div className="mb-2.5">
<div className="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
<label className="form-label max-w-56">Confirm New Pin Code</label>
<div className="input">
<input
type={showPassword.retype_password ? 'text' : 'password'}
className="form-control"
placeholder="Confirm New Pin Code"
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
disabled={isSubmitting}
/>
<button
className="btn btn-icon"
onClick={(e) => togglePassword(e, 'retype_password')}
>
<KeenIcon
icon="eye"
className={clsx('text-gray-500', { hidden: showPassword.retype_password })}
/>
<KeenIcon
icon="eye-slash"
className={clsx('text-gray-500', { hidden: !showPassword.retype_password })}
/>
</button>
</div>
</div>
<div className="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
<label className="form-label max-w-56 text-white">Confirm New Pin Code</label>
{passwordErrors.length > 0 && (
<div className="text-xs text-red-500 mt-1 ms-3">
{passwordErrors.map((error, index) => (
<p key={index}>{error}</p>
))}
</div>
)}
</div>
</div>
<div className="flex justify-end">
<button

View File

@ -18,9 +18,15 @@ interface Profile {
username: string;
}
interface PinCode {
newpincode: string;
}
interface ContextProps {
password: Password | null;
profile: Profile | null;
pincode: PinCode | null;
setPincode: (pincode: PinCode) => Promise<void>;
setPassword: (password: Password) => Promise<void>;
setProfile: (profile: Profile) => Promise<void>;
}
@ -28,8 +34,10 @@ interface ContextProps {
const initialProps: ContextProps = {
profile: null,
password: null,
pincode: null,
setPassword: async () => {},
setProfile: async () => {}
setProfile: async () => {},
setPincode: async () => {}
};
const AccountUserProfileContext = createContext<ContextProps>(initialProps);
@ -39,25 +47,29 @@ const API_URL = apiConfig.service_dashboard;
const AccountUserProfileContextProvider = ({ children }: { children: React.ReactNode }) => {
/* state */
const { login } = useAuthContext();
const [profile, setProfile] = useState<Profile | null>(null);
const [password, setPassword] = useState<Password | null>(null);
const [profile, setProfileState] = useState<Profile | null>(null);
const [password, setPasswordState] = useState<Password | null>(null);
const [pinCode, setPincodeState] = useState<PinCode | null>(null);
const [alert, setAlert] = useState({
show: false,
message: ''
});
const { PutData } = useCallApi();
const { PostData } = useCallApi();
const handleSetPassword = async (newPassword: Password) => {
setPassword(newPassword);
const { PutData, PostData } = useCallApi();
const handleError = (error: any, defaultMessage: string) => {
const errorMessage = error?.response?.data?.message || error?.message || defaultMessage;
setAlert({ show: true, message: errorMessage });
toast.error(errorMessage);
};
// Helper function to handle API errors
const handleError = (error: any, defaultMessage: string) => {
const errorMessage = error?.response?.data?.message || error?.message || defaultMessage;
setAlert({ show: true, message: errorMessage });
toast.error(errorMessage);
};
// Function to handle password update
const handleSetPassword = async (newPassword: Password) => {
setPasswordState(newPassword);
try {
// Validate the current password
const validate = await PostData(`${API_URL}/login`, {
password: newPassword.current_password,
username: newPassword.username
@ -68,6 +80,7 @@ const AccountUserProfileContextProvider = ({ children }: { children: React.React
return;
}
// Update the password
const response = await PutData(`${API_URL}/user/update_password`, {
password: newPassword.password,
retype_password: newPassword.retype_password
@ -83,9 +96,10 @@ const AccountUserProfileContextProvider = ({ children }: { children: React.React
}
};
// Function to handle profile update
const handleSetProfile = async (newProfile: Profile) => {
try {
setProfile(newProfile);
setProfileState(newProfile);
const response = await PutData(`${API_URL}/user/update_profile/`, {
name: newProfile.name,
@ -95,9 +109,19 @@ const AccountUserProfileContextProvider = ({ children }: { children: React.React
toast.success('Profile updated successfully.');
} catch (error: any) {
const errorMessage = error?.message || 'Failed to update Profile. Please try again.';
setAlert({ show: true, message: errorMessage });
toast.error(errorMessage);
handleError(error, 'Failed to update Profile. Please try again.');
}
};
// Function to handle pin code update
const handleSetPinCode = async (newPinCode: PinCode) => {
try {
setPincodeState(newPinCode);
// You can make an API call here to update the pin code in the backend if needed
toast.success('Pin code updated successfully.');
} catch (error: any) {
handleError(error, 'Failed to update Pin Code. Please try again.');
}
};
@ -106,8 +130,10 @@ const AccountUserProfileContextProvider = ({ children }: { children: React.React
value={{
password,
profile,
pincode: pinCode,
setPassword: handleSetPassword,
setProfile: handleSetProfile
setProfile: handleSetProfile,
setPincode: handleSetPinCode
}}
>
{children}

View File

@ -125,7 +125,17 @@ const DetailApprovalTransaction = () => {
<div className="grid grid-cols-2 gap-4">
<div>
<p className="text-sm text-gray-500">Transaction Date</p>
<p className="font-medium">{transactionDetails?.transaction_date}</p>
<p className="font-medium">{transactionDetails?.transaction_date
? new Date(transactionDetails.transaction_date).toLocaleDateString('en-GB', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
})
: ''}</p>
</div>
<div>
<p className="text-sm text-gray-500">Full Name</p>
@ -299,8 +309,17 @@ const DetailApprovalTransaction = () => {
<div className="grid grid-cols-2 gap-4">
<div>
<p className="text-sm text-gray-500">Transaction Date</p>
<p className="font-medium">{transactionDetails?.transaction_date}</p>
</div>
<p className="font-medium">{transactionDetails?.transaction_date
? new Date(transactionDetails.transaction_date).toLocaleDateString('en-GB', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
})
: ''}</p> </div>
<div>
<p className="text-sm text-gray-500">Full Name</p>
<p className="font-medium">
@ -607,7 +626,7 @@ const DetailApprovalTransaction = () => {
<tr className="bg-gray-100">
<th className="px-4 py-2 text-left text-sm text-gray-500">Type</th>
<th className="px-4 py-2 text-left text-sm text-gray-500">Request Date</th>
<th className="px-4 py-2 text-left text-sm text-gray-500">Request Body</th>
<th className="px-4 py-2 text-left text-sm text-gray-500">Response Date</th>
<th className="px-4 py-2 text-left text-sm text-gray-500">Response Body</th>
<th className="px-4 py-2 text-left text-sm text-gray-500">Response Code</th>
<th className="px-4 py-2 text-left text-sm text-gray-500">Request Endpoint</th>
@ -618,8 +637,29 @@ const DetailApprovalTransaction = () => {
transactionDetails.p24.map((log: { request_endpoint: string, type: string; request_date: string; response_date: string; request_body: string; response_body: string; response_code: number }, index: number) => (
<tr key={index} className="border-t">
<td className="px-4 py-2 text-sm text-gray-500">{log.type}</td>
<td className="px-4 py-2 text-sm text-gray-500">{log.request_date}</td>
<td className="px-4 py-2 text-sm text-gray-500">{log.response_date}</td>
<td className="px-4 py-2 text-sm text-gray-500">{log.request_date
? new Date(log.request_date).toLocaleDateString('en-GB', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
})
: ''}</td>
<td className="px-4 py-2 text-sm text-gray-500">{log.response_date
? new Date(log.response_date).toLocaleDateString('en-GB', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
})
: ''}</td>
<td className="px-4 py-2 text-sm text-gray-500">{log.request_body}</td>
<td className="px-4 py-2 text-sm text-gray-500">{log.response_body}</td>
<td className="px-4 py-2 text-sm text-gray-500">{log.request_endpoint ?? '-'}</td>

View File

@ -45,8 +45,8 @@ const DetailTransaction = () => {
id: selectedTransactionId
});
setTransactionDetails(response?.data);
console.log(response?.data);
console.log(selectedTransactionId);
// console.log(response?.data);
// console.log(selectedTransactionId);
} catch (error) {
console.error('Error fetching transaction', error);
}
@ -141,7 +141,20 @@ const DetailTransaction = () => {
<div className="grid grid-cols-2 gap-4">
<div>
<p className="text-sm text-gray-500">Transaction Date</p>
<p className="font-medium">{transactionDetails?.transaction_date}</p>
<p className="font-medium">
{transactionDetails?.transaction_date
? new Date(transactionDetails.transaction_date).toLocaleDateString('en-GB', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
})
: ''}
</p>
</div>
<div>
<p className="text-sm text-gray-500">Full Name</p>
@ -312,7 +325,17 @@ const DetailTransaction = () => {
<div className="grid grid-cols-2 gap-4">
<div>
<p className="text-sm text-gray-500">Transaction Date</p>
<p className="font-medium">{transactionDetails?.transaction_date}</p>
<p className="font-medium">{transactionDetails?.transaction_date
? new Date(transactionDetails.transaction_date).toLocaleDateString('en-GB', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
})
: ''}</p>
</div>
<div>
<p className="text-sm text-gray-500">Full Name</p>
@ -614,7 +637,7 @@ const DetailTransaction = () => {
<tr className="bg-gray-100">
<th className="px-4 py-2 text-left text-sm text-gray-500">Type</th>
<th className="px-4 py-2 text-left text-sm text-gray-500">Request Date</th>
<th className="px-4 py-2 text-left text-sm text-gray-500">Request Body</th>
<th className="px-4 py-2 text-left text-sm text-gray-500">Response Date</th>
<th className="px-4 py-2 text-left text-sm text-gray-500">Response Body</th>
<th className="px-4 py-2 text-left text-sm text-gray-500">Response Code</th>
<th className="px-4 py-2 text-left text-sm text-gray-500">Request Endpoint</th>
@ -625,8 +648,29 @@ const DetailTransaction = () => {
transactionDetails.p24.map((log: { request_endpoint: string, type: string; request_date: string; response_date: string; request_body: string; response_body: string; response_code: number }, index: number) => (
<tr key={index} className="border-t">
<td className="px-4 py-2 text-sm text-gray-500">{log.type}</td>
<td className="px-4 py-2 text-sm text-gray-500">{log.request_date}</td>
<td className="px-4 py-2 text-sm text-gray-500">{log.response_date}</td>
<td className="px-4 py-2 text-sm text-gray-500">{log.request_date
? new Date(log.request_date).toLocaleDateString('en-GB', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
})
: ''}</td>
<td className="px-4 py-2 text-sm text-gray-500">{log.response_date
? new Date(log.response_date).toLocaleDateString('en-GB', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
})
: ''}</td>
<td className="px-4 py-2 text-sm text-gray-500">{log.request_body}</td>
<td className="px-4 py-2 text-sm text-gray-500">{log.response_body}</td>
<td className="px-4 py-2 text-sm text-gray-500">{log.request_endpoint ?? '-'}</td>