Collect user_login and user_name in account admin UI.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
92be5ee3d2
commit
7a562c18ec
4 files changed
+43
-11
No files matched your search
@@ -39,6 +39,7 @@ const UserManagementPage: React.FC = () => {
|
|||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [notice, setNotice] = useState<string | null>(null);
|
const [notice, setNotice] = useState<string | null>(null);
|
||||||
|
|
||||||
|
const [userLogin, setUserLogin] = useState('');
|
||||||
const [userName, setUserName] = useState('');
|
const [userName, setUserName] = useState('');
|
||||||
const [status, setStatus] = useState<UserStatus>('gm');
|
const [status, setStatus] = useState<UserStatus>('gm');
|
||||||
const [password, setPassword] = useState('');
|
const [password, setPassword] = useState('');
|
||||||
@@ -80,6 +81,7 @@ const UserManagementPage: React.FC = () => {
|
|||||||
setSaving(true);
|
setSaving(true);
|
||||||
try {
|
try {
|
||||||
await api.users.create({
|
await api.users.create({
|
||||||
|
user_login: userLogin.trim(),
|
||||||
user_name: userName.trim(),
|
user_name: userName.trim(),
|
||||||
status,
|
status,
|
||||||
password,
|
password,
|
||||||
@@ -87,6 +89,7 @@ const UserManagementPage: React.FC = () => {
|
|||||||
setNotice(
|
setNotice(
|
||||||
`Akun "${userName.trim()}" dibuat. Bagikan password sementara secara aman (di luar sistem).`
|
`Akun "${userName.trim()}" dibuat. Bagikan password sementara secara aman (di luar sistem).`
|
||||||
);
|
);
|
||||||
|
setUserLogin('');
|
||||||
setUserName('');
|
setUserName('');
|
||||||
setPassword('');
|
setPassword('');
|
||||||
setStatus('gm');
|
setStatus('gm');
|
||||||
@@ -151,9 +154,20 @@ const UserManagementPage: React.FC = () => {
|
|||||||
|
|
||||||
<section className="bg-white rounded-xl shadow-sm p-5 space-y-4">
|
<section className="bg-white rounded-xl shadow-sm p-5 space-y-4">
|
||||||
<h2 className="text-base font-semibold text-gray-900">Buat pengguna baru</h2>
|
<h2 className="text-base font-semibold text-gray-900">Buat pengguna baru</h2>
|
||||||
<form onSubmit={onCreate} className="grid gap-3 sm:grid-cols-2 lg:grid-cols-4">
|
<form onSubmit={onCreate} className="grid gap-3 sm:grid-cols-2 lg:grid-cols-5">
|
||||||
<label className="block text-sm">
|
<label className="block text-sm">
|
||||||
<span className="text-gray-600">Username</span>
|
<span className="text-gray-600">Login</span>
|
||||||
|
<input
|
||||||
|
className={inputClass}
|
||||||
|
value={userLogin}
|
||||||
|
onChange={(e) => setUserLogin(e.target.value)}
|
||||||
|
required
|
||||||
|
maxLength={30}
|
||||||
|
autoComplete="off"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label className="block text-sm">
|
||||||
|
<span className="text-gray-600">Nama</span>
|
||||||
<input
|
<input
|
||||||
className={inputClass}
|
className={inputClass}
|
||||||
value={userName}
|
value={userName}
|
||||||
@@ -214,7 +228,7 @@ const UserManagementPage: React.FC = () => {
|
|||||||
<table className="min-w-full text-sm">
|
<table className="min-w-full text-sm">
|
||||||
<thead>
|
<thead>
|
||||||
<tr className="text-left text-gray-500 border-b">
|
<tr className="text-left text-gray-500 border-b">
|
||||||
<th className="py-2 pr-4 font-medium">Username</th>
|
<th className="py-2 pr-4 font-medium">Nama</th>
|
||||||
<th className="py-2 pr-4 font-medium">Role</th>
|
<th className="py-2 pr-4 font-medium">Role</th>
|
||||||
<th className="py-2 pr-4 font-medium">Managed by</th>
|
<th className="py-2 pr-4 font-medium">Managed by</th>
|
||||||
<th className="py-2 font-medium">Aksi</th>
|
<th className="py-2 font-medium">Aksi</th>
|
||||||
@@ -225,11 +239,14 @@ const UserManagementPage: React.FC = () => {
|
|||||||
const isSelf = currentUser?.id === user.id;
|
const isSelf = currentUser?.id === user.id;
|
||||||
return (
|
return (
|
||||||
<tr key={user.id} className="border-b border-gray-100 align-top">
|
<tr key={user.id} className="border-b border-gray-100 align-top">
|
||||||
<td className="py-3 pr-4 font-medium text-gray-900">
|
<td className="py-3 pr-4">
|
||||||
{user.user_name}
|
<div className="font-medium text-gray-900">
|
||||||
{isSelf ? (
|
{user.user_name}
|
||||||
<span className="ml-2 text-xs font-normal text-gray-500">(Anda)</span>
|
{isSelf ? (
|
||||||
) : null}
|
<span className="ml-2 text-xs font-normal text-gray-500">(Anda)</span>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
<div className="text-xs text-gray-500">{user.user_login}</div>
|
||||||
</td>
|
</td>
|
||||||
<td className="py-3 pr-4">
|
<td className="py-3 pr-4">
|
||||||
{!isSelf && editId === user.id ? (
|
{!isSelf && editId === user.id ? (
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ vi.mock('../../context/AuthContext.tsx', () => ({
|
|||||||
useAuth: () => ({
|
useAuth: () => ({
|
||||||
user: {
|
user: {
|
||||||
id: 9,
|
id: 9,
|
||||||
|
user_login: 'account_admin',
|
||||||
user_name: 'account_admin',
|
user_name: 'account_admin',
|
||||||
status: 'account_admin',
|
status: 'account_admin',
|
||||||
is_superuser: false,
|
is_superuser: false,
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ const {
|
|||||||
authState: {
|
authState: {
|
||||||
user: {
|
user: {
|
||||||
id: 5,
|
id: 5,
|
||||||
|
user_login: 'gm.user',
|
||||||
user_name: 'gm.user',
|
user_name: 'gm.user',
|
||||||
status: 'gm' as string,
|
status: 'gm' as string,
|
||||||
managed_by: null as number | null,
|
managed_by: null as number | null,
|
||||||
@@ -84,6 +85,7 @@ describe('SettingsPage city site registry', () => {
|
|||||||
refreshUserMock.mockReset();
|
refreshUserMock.mockReset();
|
||||||
authState.user = {
|
authState.user = {
|
||||||
id: 5,
|
id: 5,
|
||||||
|
user_login: 'gm.user',
|
||||||
user_name: 'gm.user',
|
user_name: 'gm.user',
|
||||||
status: 'gm',
|
status: 'gm',
|
||||||
managed_by: null,
|
managed_by: null,
|
||||||
@@ -127,11 +129,12 @@ describe('SettingsPage city site registry', () => {
|
|||||||
updated_at: '',
|
updated_at: '',
|
||||||
});
|
});
|
||||||
buhsMock.mockResolvedValue([
|
buhsMock.mockResolvedValue([
|
||||||
{ id: 2, user_name: 'buh', status: 'buh', created_at: '', updated_at: '' },
|
{ id: 2, user_login: 'buh', user_name: 'buh', status: 'buh', created_at: '', updated_at: '' },
|
||||||
]);
|
]);
|
||||||
usersListMock.mockResolvedValue([
|
usersListMock.mockResolvedValue([
|
||||||
{
|
{
|
||||||
id: 5,
|
id: 5,
|
||||||
|
user_login: 'gm',
|
||||||
user_name: 'gm',
|
user_name: 'gm',
|
||||||
status: 'gm',
|
status: 'gm',
|
||||||
managed_by: 2,
|
managed_by: 2,
|
||||||
@@ -188,6 +191,7 @@ describe('SettingsPage city site registry', () => {
|
|||||||
it('shows pending GM confirmation for BUH', async () => {
|
it('shows pending GM confirmation for BUH', async () => {
|
||||||
authState.user = {
|
authState.user = {
|
||||||
id: 2,
|
id: 2,
|
||||||
|
user_login: 'buh',
|
||||||
user_name: 'buh',
|
user_name: 'buh',
|
||||||
status: 'buh',
|
status: 'buh',
|
||||||
managed_by: null,
|
managed_by: null,
|
||||||
@@ -200,6 +204,7 @@ describe('SettingsPage city site registry', () => {
|
|||||||
pendingMock.mockResolvedValue([
|
pendingMock.mockResolvedValue([
|
||||||
{
|
{
|
||||||
id: 5,
|
id: 5,
|
||||||
|
user_login: 'gm.user',
|
||||||
user_name: 'gm.user',
|
user_name: 'gm.user',
|
||||||
status: 'gm',
|
status: 'gm',
|
||||||
created_at: '',
|
created_at: '',
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ const { listMock, createMock, updateMock, setPasswordMock, authState } = vi.hois
|
|||||||
authState: {
|
authState: {
|
||||||
user: {
|
user: {
|
||||||
id: 99,
|
id: 99,
|
||||||
|
user_login: 'account_admin',
|
||||||
user_name: 'account_admin',
|
user_name: 'account_admin',
|
||||||
status: 'account_admin' as string,
|
status: 'account_admin' as string,
|
||||||
created_at: '',
|
created_at: '',
|
||||||
@@ -44,6 +45,7 @@ describe('UserManagementPage', () => {
|
|||||||
setPasswordMock.mockReset();
|
setPasswordMock.mockReset();
|
||||||
authState.user = {
|
authState.user = {
|
||||||
id: 99,
|
id: 99,
|
||||||
|
user_login: 'account_admin',
|
||||||
user_name: 'account_admin',
|
user_name: 'account_admin',
|
||||||
status: 'account_admin',
|
status: 'account_admin',
|
||||||
created_at: '',
|
created_at: '',
|
||||||
@@ -52,6 +54,7 @@ describe('UserManagementPage', () => {
|
|||||||
listMock.mockResolvedValue([
|
listMock.mockResolvedValue([
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
|
user_login: 'gm1.login',
|
||||||
user_name: 'gm1',
|
user_name: 'gm1',
|
||||||
status: 'gm',
|
status: 'gm',
|
||||||
managed_by_name: 'buh1',
|
managed_by_name: 'buh1',
|
||||||
@@ -60,6 +63,7 @@ describe('UserManagementPage', () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 99,
|
id: 99,
|
||||||
|
user_login: 'account_admin',
|
||||||
user_name: 'account_admin',
|
user_name: 'account_admin',
|
||||||
status: 'account_admin',
|
status: 'account_admin',
|
||||||
managed_by_name: null,
|
managed_by_name: null,
|
||||||
@@ -69,6 +73,7 @@ describe('UserManagementPage', () => {
|
|||||||
]);
|
]);
|
||||||
createMock.mockResolvedValue({
|
createMock.mockResolvedValue({
|
||||||
id: 2,
|
id: 2,
|
||||||
|
user_login: 'newgm',
|
||||||
user_name: 'newgm',
|
user_name: 'newgm',
|
||||||
status: 'gm',
|
status: 'gm',
|
||||||
created_at: '',
|
created_at: '',
|
||||||
@@ -90,9 +95,12 @@ describe('UserManagementPage', () => {
|
|||||||
render(<UserManagementPage />);
|
render(<UserManagementPage />);
|
||||||
await screen.findByText('gm1');
|
await screen.findByText('gm1');
|
||||||
|
|
||||||
fireEvent.change(screen.getByLabelText(/Username/i), {
|
fireEvent.change(screen.getByLabelText(/^Login$/i), {
|
||||||
target: { value: 'newgm' },
|
target: { value: 'newgm' },
|
||||||
});
|
});
|
||||||
|
fireEvent.change(screen.getByLabelText(/^Nama$/i), {
|
||||||
|
target: { value: 'New GM' },
|
||||||
|
});
|
||||||
fireEvent.change(screen.getByLabelText(/Password sementara/i), {
|
fireEvent.change(screen.getByLabelText(/Password sementara/i), {
|
||||||
target: { value: 'temp123' },
|
target: { value: 'temp123' },
|
||||||
});
|
});
|
||||||
@@ -100,7 +108,8 @@ describe('UserManagementPage', () => {
|
|||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(createMock).toHaveBeenCalledWith({
|
expect(createMock).toHaveBeenCalledWith({
|
||||||
user_name: 'newgm',
|
user_login: 'newgm',
|
||||||
|
user_name: 'New GM',
|
||||||
status: 'gm',
|
status: 'gm',
|
||||||
password: 'temp123',
|
password: 'temp123',
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in new issue
Block a user