Skip to content

Commit 6198724

Browse files
committed
Add error handling to profile fields and disable save button when errors exist
1 parent b345561 commit 6198724

3 files changed

Lines changed: 72 additions & 26 deletions

File tree

web-interface/src/components/Buttons.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
import type {ReactNode} from "react";
1+
import type { ReactNode } from 'react'
22

33
const variantStyles = {
44
success: 'bg-green-200 text-green-800 hover:bg-green-300',
55
warning: 'bg-yellow-200 text-yellow-800 hover:bg-yellow-300',
66
danger: 'bg-red-200 text-red-800 hover:bg-red-300',
7-
normal: 'bg-blue-200 text-blue-800 hover:bg-blue-300'
7+
normal: 'bg-blue-200 text-blue-800 hover:bg-blue-300',
8+
disabled: 'bg-gray-200 text-gray-800',
89
}
910

1011
type ButtonVariant = keyof typeof variantStyles
@@ -13,7 +14,7 @@ export function Button({
1314
children,
1415
onClick,
1516
variant,
16-
fontSize
17+
fontSize,
1718
}: {
1819
children: ReactNode
1920
onClick: () => void
@@ -24,6 +25,7 @@ export function Button({
2425
<button
2526
onClick={onClick}
2627
className={`flex items-center gap-1.5 px-3 py-1 text-${fontSize} rounded ${variantStyles[variant]}`}
28+
disabled={variant === 'disabled'}
2729
>
2830
{children}
2931
</button>

web-interface/src/routes/jobs.tsx

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -162,26 +162,26 @@ function JobCard({
162162
return (
163163
<Card>
164164
<CardHeader header={job.name}>
165-
<Button
166-
onClick={() => onStart(job.id)}
167-
variant="success"
168-
fontSize="xs"
169-
>Start</Button>
165+
<Button onClick={() => onStart(job.id)} variant="success" fontSize="xs">
166+
Start
167+
</Button>
170168
<Button
171169
onClick={() => onShutdown(job.id)}
172170
variant="warning"
173171
fontSize="xs"
174-
>Shutdown</Button>
172+
>
173+
Shutdown
174+
</Button>
175175
<Button
176176
onClick={() => onRestart(job.id)}
177177
variant="warning"
178178
fontSize="xs"
179-
>Restart</Button>
180-
<Button
181-
onClick={() => onDelete(job.id)}
182-
variant="danger"
183-
fontSize="xs"
184-
>Delete</Button>
179+
>
180+
Restart
181+
</Button>
182+
<Button onClick={() => onDelete(job.id)} variant="danger" fontSize="xs">
183+
Delete
184+
</Button>
185185
</CardHeader>
186186

187187
{/* Info grid */}

web-interface/src/routes/profile.tsx

Lines changed: 55 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { createFileRoute } from '@tanstack/react-router'
22
import { SquarePen } from 'lucide-react'
33
import { getUser } from '#/util.ts'
44
import { useImmer } from 'use-immer'
5-
import {Button} from "#/components/Buttons.tsx";
5+
import { Button } from '#/components/Buttons.tsx'
66

77
export const Route = createFileRoute('/profile')({
88
component: ProfilePage,
@@ -14,11 +14,13 @@ function ProfileField({
1414
value,
1515
type = 'text',
1616
onChange,
17+
error,
1718
}: {
1819
label: string
1920
value: string
2021
type?: string
2122
onChange: (val: string) => void
23+
error?: string
2224
}) {
2325
return (
2426
<div>
@@ -27,8 +29,9 @@ function ProfileField({
2729
type={type}
2830
value={value}
2931
onChange={(e) => onChange(e.target.value)}
30-
className="w-full border border-gray-300 rounded-lg px-4 py-2 text-sm disabled:bg-white disabled:text-gray-700"
32+
className={`w-full border ${error ? 'border-red-600' : 'border-gray-300'} rounded-lg px-4 py-2 text-sm disabled:bg-white disabled:text-gray-700`}
3133
/>
34+
<p className="text-red-600 min-h-6">{error}</p>
3235
</div>
3336
)
3437
}
@@ -52,22 +55,59 @@ function ProfilePage() {
5255
}
5356

5457
function handleCancel() {
55-
const confirmCancel = confirm("Are you sure you want to cancel?");
58+
const confirmCancel = confirm('Are you sure you want to cancel?')
5659

5760
if (confirmCancel) {
5861
setUser({
5962
...loaderData,
6063
password: '',
6164
confirmPassword: '',
62-
});
65+
})
6366
}
6467
}
6568

69+
function getUserErrors(field: string): string | undefined {
70+
switch (field) {
71+
case 'username':
72+
case 'role':
73+
case 'email':
74+
if (user[field] === '') {
75+
return `Field cannot be empty.`
76+
}
77+
break
78+
case 'password':
79+
if (user['password'].length !== 0) {
80+
if (user['password'].length < 8) {
81+
return 'Password must be at least 8 characters long'
82+
}
83+
}
84+
break
85+
case 'confirmPassword':
86+
if (
87+
user['password'].length !== 0 &&
88+
user['confirmPassword'] !== user['password']
89+
) {
90+
return 'Passwords do not match.'
91+
}
92+
break
93+
}
94+
}
95+
96+
function hasError(): boolean {
97+
for (const field in user) {
98+
if (getUserErrors(field)) {
99+
return true
100+
}
101+
}
102+
103+
return false
104+
}
105+
66106
return (
67107
<div className="w-fit mx-auto py-8 px-8">
68108
<div className="flex gap-16 items-start">
69109
{/* Left: form fields */}
70-
<div className="flex-1 min-w-80 flex flex-col gap-5">
110+
<div className="flex-1 min-w-80 flex flex-col">
71111
<ProfileField
72112
label="Username"
73113
value={user.username}
@@ -76,6 +116,7 @@ function ProfilePage() {
76116
draft.username = username
77117
})
78118
}
119+
error={getUserErrors('username')}
79120
/>
80121
<ProfileField
81122
label="Role"
@@ -85,6 +126,7 @@ function ProfilePage() {
85126
draft.role = role
86127
})
87128
}
129+
error={getUserErrors('role')}
88130
/>
89131
<ProfileField
90132
label="Email"
@@ -95,6 +137,7 @@ function ProfilePage() {
95137
draft.email = email
96138
})
97139
}
140+
error={getUserErrors('email')}
98141
/>
99142
<ProfileField
100143
label="Password"
@@ -105,6 +148,7 @@ function ProfilePage() {
105148
draft.password = password
106149
})
107150
}
151+
error={getUserErrors('password')}
108152
/>
109153
<ProfileField
110154
label="Confirm Password"
@@ -115,18 +159,18 @@ function ProfilePage() {
115159
draft.confirmPassword = confirmPassword
116160
})
117161
}
162+
error={getUserErrors('confirmPassword')}
118163
/>
119164

120165
<div className="flex gap-3 mt-2">
121166
<Button
122167
onClick={handleSave}
123-
variant="normal"
168+
variant={hasError() ? 'disabled' : 'normal'}
124169
fontSize="base"
125-
>Save</Button>
126-
<Button
127-
onClick={handleCancel}
128-
variant="danger"
129-
fontSize="base">
170+
>
171+
Save
172+
</Button>
173+
<Button onClick={handleCancel} variant="danger" fontSize="base">
130174
Cancel
131175
</Button>
132176
</div>

0 commit comments

Comments
 (0)