Skip to content

Commit 53fe1cd

Browse files
committed
refactor: Update form types in admin, user template, and settings modals to use input types for improved type safety
1 parent 7a61937 commit 53fe1cd

11 files changed

+55
-42
lines changed

dashboard/src/components/dialogs/admin-modal.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ interface AdminModalProps {
2525
onOpenChange: (open: boolean) => void
2626
editingAdmin?: boolean
2727
editingAdminUserName: string
28-
form: UseFormReturn<AdminFormValues>
28+
form: UseFormReturn<AdminFormValuesInput>
2929
}
3030

3131
const passwordValidation = z.string().refine(
@@ -131,6 +131,7 @@ export const adminFormSchema = z
131131
}
132132
})
133133

134+
export type AdminFormValuesInput = z.input<typeof adminFormSchema>
134135
export type AdminFormValues = z.infer<typeof adminFormSchema>
135136
export default function AdminModal({ isDialogOpen, onOpenChange, editingAdminUserName, editingAdmin, form }: AdminModalProps) {
136137
const { t } = useTranslation()
@@ -157,10 +158,10 @@ export default function AdminModal({ isDialogOpen, onOpenChange, editingAdminUse
157158
onOpenChange(open)
158159
}
159160

160-
const onSubmit = async (values: AdminFormValues) => {
161+
const onSubmit = async (values: AdminFormValuesInput) => {
161162
try {
162163
const editData = {
163-
is_sudo: values.is_sudo,
164+
is_sudo: values.is_sudo ?? false,
164165
password: values.password || undefined,
165166
is_disabled: values.is_disabled,
166167
discord_webhook: values.discord_webhook,
@@ -187,6 +188,7 @@ export default function AdminModal({ isDialogOpen, onOpenChange, editingAdminUse
187188
if (!values.password) return
188189
const createData = {
189190
...values,
191+
is_sudo: values.is_sudo ?? false,
190192
password: values.password, // Ensure password is present
191193
}
192194
await addAdminMutation.mutateAsync({

dashboard/src/components/dialogs/shortcuts-modal.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ const QuickActionsModal = ({ open, onClose, onCreateUser, onCreateGroup, onCreat
3838
icon: <UsersIcon className="h-5 w-5" />,
3939
action: onCreateUser,
4040
disabled: false,
41-
category: 'users',
41+
category: 'users' as const,
4242
},
4343
// Only show these actions for sudo admins
4444
...(isSudo
@@ -50,7 +50,7 @@ const QuickActionsModal = ({ open, onClose, onCreateUser, onCreateGroup, onCreat
5050
icon: <Users2 className="h-5 w-5" />,
5151
action: onCreateGroup,
5252
disabled: false,
53-
category: 'users',
53+
category: 'users' as const,
5454
},
5555
{
5656
id: '3',
@@ -59,7 +59,7 @@ const QuickActionsModal = ({ open, onClose, onCreateUser, onCreateGroup, onCreat
5959
icon: <LayoutTemplate className="h-5 w-5" />,
6060
action: onCreateTemplate || (() => {}),
6161
disabled: !onCreateTemplate,
62-
category: 'users',
62+
category: 'users' as const,
6363
},
6464
{
6565
id: '4',
@@ -68,7 +68,7 @@ const QuickActionsModal = ({ open, onClose, onCreateUser, onCreateGroup, onCreat
6868
icon: <ListTodo className="h-5 w-5" />,
6969
action: onCreateHost,
7070
disabled: false,
71-
category: 'system',
71+
category: 'system' as const,
7272
},
7373
{
7474
id: '5',
@@ -77,7 +77,7 @@ const QuickActionsModal = ({ open, onClose, onCreateUser, onCreateGroup, onCreat
7777
icon: <Share2Icon className="h-5 w-5" />,
7878
action: onCreateNode,
7979
disabled: false,
80-
category: 'system',
80+
category: 'system' as const,
8181
},
8282
{
8383
id: '6',
@@ -86,7 +86,7 @@ const QuickActionsModal = ({ open, onClose, onCreateUser, onCreateGroup, onCreat
8686
icon: <Cpu className="h-5 w-5" />,
8787
action: onCreateCore || (() => {}),
8888
disabled: !onCreateCore,
89-
category: 'system',
89+
category: 'system' as const,
9090
},
9191
// Admin Management
9292
{
@@ -96,7 +96,7 @@ const QuickActionsModal = ({ open, onClose, onCreateUser, onCreateGroup, onCreat
9696
icon: <UserCog className="h-5 w-5" />,
9797
action: onCreateAdmin || (() => {}),
9898
disabled: !onCreateAdmin,
99-
category: 'management',
99+
category: 'management' as const,
100100
},
101101
]
102102
: []),

dashboard/src/components/dialogs/user-template-modal.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,13 @@ export const userTemplateFormSchema = z.object({
4343
reset_usages: z.boolean().optional(),
4444
})
4545

46+
export type UserTemplatesFromValueInput = z.input<typeof userTemplateFormSchema>
4647
export type UserTemplatesFromValue = z.infer<typeof userTemplateFormSchema>
4748

4849
interface UserTemplatesModalprops {
4950
isDialogOpen: boolean
5051
onOpenChange: (open: boolean) => void
51-
form: UseFormReturn<UserTemplatesFromValue>
52+
form: UseFormReturn<UserTemplatesFromValueInput>
5253
editingUserTemplate: boolean
5354
editingUserTemplateId?: number
5455
}
@@ -62,9 +63,10 @@ export default function UserTemplateModal({ isDialogOpen, onOpenChange, form, ed
6263
const [timeType, setTimeType] = useState<'seconds' | 'hours' | 'days'>('seconds')
6364
const [loading, setLoading] = useState(false)
6465

65-
const onSubmit = async (values: UserTemplatesFromValue) => {
66+
const onSubmit = async (values: UserTemplatesFromValueInput) => {
6667
setLoading(true)
6768
try {
69+
const status = values.status ?? UserStatusCreate.active
6870
// Build payload according to UserTemplateCreate interface
6971
const submitData = {
7072
name: values.name,
@@ -73,8 +75,8 @@ export default function UserTemplateModal({ isDialogOpen, onOpenChange, form, ed
7375
username_prefix: values.username_prefix || '',
7476
username_suffix: values.username_suffix || '',
7577
group_ids: values.groups, // map groups to group_ids
76-
status: values.status,
77-
on_hold_timeout: values.status === UserStatusCreate.on_hold ? values.on_hold_timeout : undefined,
78+
status,
79+
on_hold_timeout: status === UserStatusCreate.on_hold ? values.on_hold_timeout : undefined,
7880
data_limit_reset_strategy: values.data_limit ? values.data_limit_reset_strategy : undefined,
7981
reset_usages: values.reset_usages,
8082
extra_settings:

dashboard/src/pages/_dashboard._index.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import AdminStatisticsCard from '@/components/dashboard/admin-statistics-card'
22
import DashboardStatistics from '@/components/dashboard/dashboard-statistics'
33
import WorkersHealthCard from '@/components/dashboard/workers-health-card'
4-
import AdminModal, { adminFormSchema, AdminFormValues } from '@/components/dialogs/admin-modal'
4+
import AdminModal, { adminFormSchema, AdminFormValuesInput } from '@/components/dialogs/admin-modal'
55
import { coreConfigFormSchema, CoreConfigFormValues } from '@/components/dialogs/core-config-modal'
66
import GroupModal, { groupFormSchema, GroupFormValues } from '@/components/dialogs/group-modal'
77
import HostModal from '@/components/dialogs/host-modal'
88
import NodeModal, { nodeFormSchema, NodeFormValues } from '@/components/dialogs/node-modal'
99
import QuickActionsModal from '@/components/dialogs/shortcuts-modal'
1010
import UserModal from '@/components/dialogs/user-modal'
11-
import UserTemplateModal, { userTemplateFormSchema, UserTemplatesFromValue } from '@/components/dialogs/user-template-modal'
11+
import UserTemplateModal, { userTemplateFormSchema, UserTemplatesFromValueInput } from '@/components/dialogs/user-template-modal'
1212
import { HostFormValues } from '@/components/hosts/hosts-list'
1313
import { Avatar, AvatarFallback } from '@/components/ui/avatar'
1414
import { Button } from '@/components/ui/button'
@@ -148,7 +148,7 @@ const Dashboard = () => {
148148
},
149149
})
150150

151-
const adminForm = useForm<AdminFormValues>({
151+
const adminForm = useForm<AdminFormValuesInput>({
152152
resolver: zodResolver(adminFormSchema),
153153
defaultValues: {
154154
username: '',
@@ -175,7 +175,7 @@ const Dashboard = () => {
175175
},
176176
})
177177

178-
const templateForm = useForm<UserTemplatesFromValue>({
178+
const templateForm = useForm<UserTemplatesFromValueInput>({
179179
resolver: zodResolver(userTemplateFormSchema),
180180
defaultValues: {
181181
name: '',

dashboard/src/pages/_dashboard.admins.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@ import PageHeader from '@/components/layout/page-header'
66
import { Separator } from '@/components/ui/separator'
77
import { toast } from 'sonner'
88
import AdminsTable from '@/components/admins/admins-table'
9-
import AdminModal, { adminFormSchema, AdminFormValues } from '@/components/dialogs/admin-modal'
9+
import AdminModal, { adminFormSchema, AdminFormValuesInput } from '@/components/dialogs/admin-modal'
1010
import { useActivateAllDisabledUsers, useDisableAllActiveUsers, useModifyAdmin, useRemoveAdmin, useResetAdminUsage } from '@/service/api'
1111
import type { AdminDetails } from '@/service/api'
1212
import AdminsStatistics from '@/components/admins/admin-statistics'
1313
import { zodResolver } from '@hookform/resolvers/zod'
1414
import { queryClient } from '@/utils/query-client.ts'
1515
import useDynamicErrorHandler from '@/hooks/use-dynamic-errors'
1616

17-
const initialDefaultValues: Partial<AdminFormValues> = {
17+
const initialDefaultValues: Partial<AdminFormValuesInput> = {
1818
username: '',
1919
is_sudo: false,
2020
password: '',
@@ -41,7 +41,7 @@ export default function AdminsPage() {
4141
const [editingAdmin, setEditingAdmin] = useState<Partial<AdminDetails> | null>(null)
4242
const [isDialogOpen, setIsDialogOpen] = useState(false)
4343
const [adminCounts, setAdminCounts] = useState<{ total: number; active: number; disabled: number } | null>(null)
44-
const form = useForm<AdminFormValues>({
44+
const form = useForm<AdminFormValuesInput>({
4545
resolver: zodResolver(adminFormSchema),
4646
defaultValues: initialDefaultValues,
4747
})

dashboard/src/pages/_dashboard.settings.discord.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@ const discordSettingsSchema = z.object({
1919
proxy_url: z.string().url('Please enter a valid URL').optional().or(z.literal('')),
2020
})
2121

22-
type DiscordSettingsForm = z.infer<typeof discordSettingsSchema>
22+
type DiscordSettingsFormInput = z.input<typeof discordSettingsSchema>
2323

2424
export default function DiscordSettings() {
2525
const { t } = useTranslation()
2626
const { settings, isLoading, error, updateSettings, isSaving } = useSettingsContext()
2727

28-
const form = useForm<DiscordSettingsForm>({
28+
const form = useForm<DiscordSettingsFormInput>({
2929
resolver: zodResolver(discordSettingsSchema),
3030
defaultValues: {
3131
enable: false,
@@ -49,12 +49,13 @@ export default function DiscordSettings() {
4949
}
5050
}, [settings, form])
5151

52-
const onSubmit = async (data: DiscordSettingsForm) => {
52+
const onSubmit = async (data: DiscordSettingsFormInput) => {
5353
try {
5454
// Filter out empty values and prepare the payload
5555
const filteredData: any = {
5656
discord: {
5757
...data,
58+
enable: data.enable ?? false,
5859
// Convert empty strings to undefined
5960
token: data.token?.trim() || undefined,
6061
proxy_url: data.proxy_url?.trim() || undefined,

dashboard/src/pages/_dashboard.settings.general.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,15 @@ const generalSettingsSchema = z.object({
2121
default_method: z.string().default(''),
2222
})
2323

24-
type GeneralSettingsForm = z.infer<typeof generalSettingsSchema>
24+
type GeneralSettingsFormInput = z.input<typeof generalSettingsSchema>
2525

2626
export default function General() {
2727
const { t } = useTranslation()
2828
const { settings, isLoading, error, updateSettings, isSaving } = useSettingsContext()
2929
const [isReconnectAllDialogOpen, setIsReconnectAllDialogOpen] = useState(false)
3030
const reconnectAllNodeMutation = useReconnectAllNode()
3131

32-
const form = useForm<GeneralSettingsForm>({
32+
const form = useForm<GeneralSettingsFormInput>({
3333
resolver: zodResolver(generalSettingsSchema),
3434
defaultValues: {
3535
default_flow: '',
@@ -44,7 +44,7 @@ export default function General() {
4444
})
4545
}, [settings])
4646

47-
const onSubmit = async (data: GeneralSettingsForm) => {
47+
const onSubmit = async (data: GeneralSettingsFormInput) => {
4848
try {
4949
// Filter out empty values and prepare the payload
5050
const filteredData: any = {
@@ -149,7 +149,7 @@ export default function General() {
149149
)
150150
}
151151

152-
const clearField = (field: keyof GeneralSettingsForm) => {
152+
const clearField = (field: keyof GeneralSettingsFormInput) => {
153153
return (e: React.MouseEvent<HTMLButtonElement>) => {
154154
e.preventDefault()
155155
e.stopPropagation()

dashboard/src/pages/_dashboard.settings.telegram.tsx

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ const telegramSettingsSchema = z.object({
5858
for_admins_only: z.boolean().default(true),
5959
})
6060

61-
type TelegramSettingsForm = z.infer<typeof telegramSettingsSchema>
61+
type TelegramSettingsFormInput = z.input<typeof telegramSettingsSchema>
6262

6363
// Helper function to get current panel URL
6464
const getCurrentPanelUrl = () => {
@@ -68,15 +68,18 @@ const getCurrentPanelUrl = () => {
6868
}
6969

7070
// Helper to map frontend Telegram form data to backend payload
71-
function mapTelegramFormToPayload(data: TelegramSettingsForm) {
71+
function mapTelegramFormToPayload(data: TelegramSettingsFormInput) {
7272
const mapped = {
7373
...data,
74+
enable: data.enable ?? false,
75+
method: data.method ?? 'webhook',
76+
mini_app_login: data.mini_app_login ?? false,
77+
for_admins_only: data.for_admins_only ?? true,
7478
token: data.token?.trim() || undefined,
7579
webhook_url: data.webhook_url?.trim() || undefined,
7680
webhook_secret: data.webhook_secret?.trim() || undefined,
7781
proxy_url: data.proxy_url?.trim() || undefined,
7882
mini_app_web_url: data.mini_app_url?.trim() || undefined,
79-
for_admins_only: data.for_admins_only,
8083
}
8184
delete mapped.mini_app_url
8285
return { telegram: mapped }
@@ -87,7 +90,7 @@ export default function TelegramSettings() {
8790
const { settings, isLoading, error, updateSettings, isSaving } = useSettingsContext()
8891
const [popoverOpen, setPopoverOpen] = useState(false)
8992

90-
const form = useForm<TelegramSettingsForm>({
93+
const form = useForm<TelegramSettingsFormInput>({
9194
resolver: zodResolver(telegramSettingsSchema),
9295
defaultValues: {
9396
enable: false,
@@ -124,7 +127,7 @@ export default function TelegramSettings() {
124127
}
125128
}, [settings, form])
126129

127-
const onSubmit = async (data: TelegramSettingsForm) => {
130+
const onSubmit = async (data: TelegramSettingsFormInput) => {
128131
try {
129132
// Use the mapping helper
130133
const filteredData = mapTelegramFormToPayload(data)

dashboard/src/pages/_dashboard.settings.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ const sudoTabs: Tab[] = [
5151
const nonSudoTabs: Tab[] = [{ id: 'theme', label: 'theme.title', icon: Palette, url: '/settings/theme' }]
5252

5353
export default function Settings() {
54-
const { t, i18n } = useTranslation()
54+
const { t } = useTranslation()
5555
const location = useLocation()
5656
const navigate = useNavigate()
5757
const { admin } = useAdmin()
@@ -203,7 +203,7 @@ export default function Settings() {
203203
settings: is_sudo ? settings || {} : {}, // Non-sudo admins don't need settings data
204204
isLoading: is_sudo ? isLoading : false,
205205
error: is_sudo ? error : null,
206-
updateSettings: is_sudo ? handleUpdateSettings : () => {}, // No-op for non-sudo admins
206+
updateSettings: is_sudo ? handleUpdateSettings : () => { }, // No-op for non-sudo admins
207207
isSaving: is_sudo ? isSaving : false,
208208
}),
209209
[is_sudo, settings, isLoading, error, isSaving, handleUpdateSettings],

dashboard/src/pages/_dashboard.settings.webhook.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,13 @@ const webhookSettingsSchema = z.object({
3232
proxy_url: z.string().url('Please enter a valid URL').optional().or(z.literal('')),
3333
})
3434

35-
type WebhookSettingsForm = z.infer<typeof webhookSettingsSchema>
35+
type WebhookSettingsFormInput = z.input<typeof webhookSettingsSchema>
3636

3737
export default function WebhookSettings() {
3838
const { t } = useTranslation()
3939
const { settings, isLoading, error, updateSettings, isSaving } = useSettingsContext()
4040

41-
const form = useForm<WebhookSettingsForm>({
41+
const form = useForm<WebhookSettingsFormInput>({
4242
resolver: zodResolver(webhookSettingsSchema),
4343
defaultValues: {
4444
enable: false,
@@ -97,16 +97,21 @@ export default function WebhookSettings() {
9797
}
9898
}, [settings, form])
9999

100-
const onSubmit = async (data: WebhookSettingsForm) => {
100+
const onSubmit = async (data: WebhookSettingsFormInput) => {
101101
try {
102102
// Filter out empty values and prepare the payload
103103
const filteredData: any = {
104104
webhook: {
105105
...data,
106+
enable: data.enable ?? false,
107+
days_left: data.days_left ?? [],
108+
usage_percent: data.usage_percent ?? [],
109+
timeout: data.timeout ?? 30,
110+
recurrent: data.recurrent ?? 3,
106111
// Convert empty strings to undefined
107112
proxy_url: data.proxy_url?.trim() || undefined,
108113
// Ensure arrays are properly formatted
109-
webhooks: data.webhooks.map(webhook => ({
114+
webhooks: (data.webhooks ?? []).map(webhook => ({
110115
url: webhook.url.trim(),
111116
secret: webhook.secret.trim(),
112117
})),

0 commit comments

Comments
 (0)