@hed-hog/core 0.0.185 → 0.0.190
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/hedhog/frontend/app/account/2fa/page.tsx.ejs +5 -0
- package/hedhog/frontend/app/account/accounts/page.tsx.ejs +5 -0
- package/hedhog/frontend/app/account/components/active-sessions.tsx.ejs +356 -0
- package/hedhog/frontend/app/account/components/change-email-form.tsx.ejs +379 -0
- package/hedhog/frontend/app/account/components/change-password-form.tsx.ejs +184 -0
- package/hedhog/frontend/app/account/components/connected-accounts.tsx.ejs +144 -0
- package/hedhog/frontend/app/account/components/email-request-dialog.tsx.ejs +96 -0
- package/hedhog/frontend/app/account/components/mfa-add-buttons.tsx.ejs +43 -0
- package/hedhog/frontend/app/account/components/mfa-method-card.tsx.ejs +115 -0
- package/hedhog/frontend/app/account/components/mfa-setup-dialog.tsx.ejs +236 -0
- package/hedhog/frontend/app/account/components/profile-form.tsx.ejs +209 -0
- package/hedhog/frontend/app/account/components/recovery-codes-dialog.tsx.ejs +192 -0
- package/hedhog/frontend/app/account/components/regenerate-codes-dialog.tsx.ejs +372 -0
- package/hedhog/frontend/app/account/components/remove-mfa-dialog.tsx.ejs +337 -0
- package/hedhog/frontend/app/account/components/two-factor-auth.tsx.ejs +393 -0
- package/hedhog/frontend/app/account/components/verify-before-add-dialog.tsx.ejs +332 -0
- package/hedhog/frontend/app/account/email/page.tsx.ejs +5 -0
- package/hedhog/frontend/app/account/hooks/use-mfa-methods.ts.ejs +27 -0
- package/hedhog/frontend/app/account/hooks/use-mfa-setup.ts.ejs +461 -0
- package/hedhog/frontend/app/account/layout.tsx.ejs +105 -0
- package/hedhog/frontend/app/account/lib/mfa-utils.tsx.ejs +37 -0
- package/hedhog/frontend/app/account/page.tsx.ejs +5 -0
- package/hedhog/frontend/app/account/password/page.tsx.ejs +5 -0
- package/hedhog/frontend/app/account/profile/page.tsx.ejs +5 -0
- package/hedhog/frontend/app/account/sessions/page.tsx.ejs +5 -0
- package/hedhog/frontend/app/configurations/[slug]/components/setting-field.tsx.ejs +490 -0
- package/hedhog/frontend/app/configurations/[slug]/page.tsx.ejs +62 -0
- package/hedhog/frontend/app/configurations/layout.tsx.ejs +316 -0
- package/hedhog/frontend/app/configurations/page.tsx.ejs +35 -0
- package/hedhog/frontend/app/dashboard/[slug]/dashboard-content.tsx.ejs +351 -0
- package/hedhog/frontend/app/dashboard/[slug]/page.tsx.ejs +11 -0
- package/hedhog/frontend/app/dashboard/[slug]/types.ts.ejs +62 -0
- package/hedhog/frontend/app/dashboard/[slug]/widget-renderer.tsx.ejs +45 -0
- package/hedhog/frontend/app/dashboard/dashboard.css.ejs +196 -0
- package/hedhog/frontend/app/dashboard/management/page.tsx.ejs +63 -0
- package/hedhog/frontend/app/dashboard/management/tabs/component-roles-tab.tsx.ejs +516 -0
- package/hedhog/frontend/app/dashboard/management/tabs/components-tab.tsx.ejs +753 -0
- package/hedhog/frontend/app/dashboard/management/tabs/dashboard-roles-tab.tsx.ejs +516 -0
- package/hedhog/frontend/app/dashboard/management/tabs/dashboards-tab.tsx.ejs +489 -0
- package/hedhog/frontend/app/dashboard/management/tabs/items-tab.tsx.ejs +621 -0
- package/hedhog/frontend/app/dashboard/page.tsx.ejs +14 -0
- package/hedhog/frontend/app/mail/log/page.tsx.ejs +312 -0
- package/hedhog/frontend/app/mail/template/page.tsx.ejs +1177 -0
- package/hedhog/frontend/app/preferences/page.tsx.ejs +448 -0
- package/hedhog/frontend/app/roles/menus.tsx.ejs +504 -0
- package/hedhog/frontend/app/roles/page.tsx.ejs +814 -0
- package/hedhog/frontend/app/roles/routes.tsx.ejs +397 -0
- package/hedhog/frontend/app/roles/users.tsx.ejs +306 -0
- package/hedhog/frontend/app/users/active-session.tsx.ejs +159 -0
- package/hedhog/frontend/app/users/identifiers.tsx.ejs +279 -0
- package/hedhog/frontend/app/users/page.tsx.ejs +1257 -0
- package/hedhog/frontend/app/users/permissions.tsx.ejs +155 -0
- package/hedhog/frontend/messages/en.json +1080 -0
- package/hedhog/frontend/messages/pt.json +1135 -0
- package/package.json +4 -4
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { PageHeader } from '@/components/entity-list';
|
|
4
|
+
import { LanguageSelector } from '@/components/language-selector';
|
|
5
|
+
import { Card, CardContent } from '@/components/ui/card';
|
|
6
|
+
import { Form } from '@/components/ui/form';
|
|
7
|
+
import {
|
|
8
|
+
Select,
|
|
9
|
+
SelectContent,
|
|
10
|
+
SelectItem,
|
|
11
|
+
SelectTrigger,
|
|
12
|
+
SelectValue,
|
|
13
|
+
} from '@/components/ui/select';
|
|
14
|
+
import { PaginatedResult } from '@hed-hog/api-pagination';
|
|
15
|
+
import { Setting, SettingList } from '@hed-hog/api-types';
|
|
16
|
+
import { useApp, useQuery } from '@hed-hog/next-app-provider';
|
|
17
|
+
import { zodResolver } from '@hookform/resolvers/zod';
|
|
18
|
+
import { Loader } from 'lucide-react';
|
|
19
|
+
import { useTranslations } from 'next-intl';
|
|
20
|
+
import { useForm } from 'react-hook-form';
|
|
21
|
+
import { z } from 'zod';
|
|
22
|
+
|
|
23
|
+
export default function PreferencesPage() {
|
|
24
|
+
const {
|
|
25
|
+
currentTheme,
|
|
26
|
+
setCurrentTheme,
|
|
27
|
+
request,
|
|
28
|
+
getSettingValue,
|
|
29
|
+
setSettingValue,
|
|
30
|
+
showToastHandler,
|
|
31
|
+
currentLocaleCode,
|
|
32
|
+
} = useApp();
|
|
33
|
+
const t = useTranslations('core.PreferencesPage');
|
|
34
|
+
|
|
35
|
+
const applyThemeSettings = async (
|
|
36
|
+
themeMode?: 'light' | 'dark' | 'system'
|
|
37
|
+
) => {
|
|
38
|
+
try {
|
|
39
|
+
const response: any = await request({
|
|
40
|
+
url: '/setting/initial',
|
|
41
|
+
method: 'GET',
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const newSettings = response.data?.setting || {};
|
|
45
|
+
localStorage.setItem('settings', JSON.stringify(newSettings));
|
|
46
|
+
|
|
47
|
+
const root = document.documentElement;
|
|
48
|
+
const effectiveTheme = themeMode || currentTheme || 'system';
|
|
49
|
+
let resolvedTheme: 'light' | 'dark' = 'light';
|
|
50
|
+
|
|
51
|
+
if (effectiveTheme === 'system') {
|
|
52
|
+
const prefersDark = window.matchMedia(
|
|
53
|
+
'(prefers-color-scheme: dark)'
|
|
54
|
+
).matches;
|
|
55
|
+
resolvedTheme = prefersDark ? 'dark' : 'light';
|
|
56
|
+
} else {
|
|
57
|
+
resolvedTheme = effectiveTheme as 'light' | 'dark';
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
root.classList.remove('light', 'dark');
|
|
61
|
+
root.classList.add(resolvedTheme);
|
|
62
|
+
|
|
63
|
+
const existingStyle = document.getElementById('theme-custom-styles');
|
|
64
|
+
if (existingStyle) {
|
|
65
|
+
existingStyle.remove();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const styleTag = document.createElement('style');
|
|
69
|
+
styleTag.id = 'theme-custom-styles';
|
|
70
|
+
let cssRules = '';
|
|
71
|
+
|
|
72
|
+
const addRule = (varName: string, settingKey: string) => {
|
|
73
|
+
const hexColor = newSettings[settingKey];
|
|
74
|
+
if (
|
|
75
|
+
hexColor &&
|
|
76
|
+
typeof hexColor === 'string' &&
|
|
77
|
+
hexColor.startsWith('#')
|
|
78
|
+
) {
|
|
79
|
+
cssRules += ` ${varName}: ${hexColor} !important;\n`;
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
if (resolvedTheme === 'light') {
|
|
84
|
+
cssRules += '@supports (color: lab(0% 0 0)) {\n';
|
|
85
|
+
cssRules += ' html.light, .light, :root.light {\n';
|
|
86
|
+
addRule('--primary', 'theme-primary-light');
|
|
87
|
+
addRule('--bprogress-color', 'theme-primary-light');
|
|
88
|
+
addRule('--primary-foreground', 'theme-primary-foreground-light');
|
|
89
|
+
addRule('--secondary', 'theme-secondary-light');
|
|
90
|
+
addRule('--secondary-foreground', 'theme-secondary-foreground-light');
|
|
91
|
+
addRule('--accent', 'theme-accent-light');
|
|
92
|
+
addRule('--accent-foreground', 'theme-accent-foreground-light');
|
|
93
|
+
addRule('--muted', 'theme-muted-light');
|
|
94
|
+
addRule('--muted-foreground', 'theme-muted-foreground-light');
|
|
95
|
+
addRule('--background', 'theme-background-light');
|
|
96
|
+
addRule('--foreground', 'theme-background-foreground-light');
|
|
97
|
+
addRule('--card', 'theme-card-light');
|
|
98
|
+
addRule('--card-foreground', 'theme-card-foreground-light');
|
|
99
|
+
} else {
|
|
100
|
+
cssRules += '@supports (color: lab(0% 0 0)) {\n';
|
|
101
|
+
cssRules += ' html.dark, .dark, :root.dark {\n';
|
|
102
|
+
addRule('--primary', 'theme-primary-dark');
|
|
103
|
+
addRule('--bprogress-color', 'theme-primary-dark');
|
|
104
|
+
addRule('--primary-foreground', 'theme-primary-foreground-dark');
|
|
105
|
+
addRule('--secondary', 'theme-secondary-dark');
|
|
106
|
+
addRule('--secondary-foreground', 'theme-secondary-foreground-dark');
|
|
107
|
+
addRule('--accent', 'theme-accent-dark');
|
|
108
|
+
addRule('--accent-foreground', 'theme-accent-foreground-dark');
|
|
109
|
+
addRule('--muted', 'theme-muted-dark');
|
|
110
|
+
addRule('--muted-foreground', 'theme-muted-foreground-dark');
|
|
111
|
+
addRule('--background', 'theme-background-dark');
|
|
112
|
+
addRule('--foreground', 'theme-background-foreground-dark');
|
|
113
|
+
addRule('--card', 'theme-card-dark');
|
|
114
|
+
addRule('--card-foreground', 'theme-card-foreground-dark');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (newSettings['theme-radius']) {
|
|
118
|
+
cssRules += ` --radius: ${newSettings['theme-radius']}rem !important;\n`;
|
|
119
|
+
}
|
|
120
|
+
if (newSettings['theme-text-size']) {
|
|
121
|
+
cssRules += ` font-size: ${newSettings['theme-text-size']}rem !important;\n`;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
cssRules += ' }\n';
|
|
125
|
+
cssRules += '}\n';
|
|
126
|
+
|
|
127
|
+
if (newSettings['theme-font']) {
|
|
128
|
+
cssRules += `:root {\n`;
|
|
129
|
+
cssRules += ` --font-sans: ${newSettings['theme-font']} !important;\n`;
|
|
130
|
+
cssRules += `}\n`;
|
|
131
|
+
cssRules += `html {\n`;
|
|
132
|
+
cssRules += ` font-family: ${newSettings['theme-font']} !important;\n`;
|
|
133
|
+
cssRules += `}\n`;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
styleTag.textContent = cssRules;
|
|
137
|
+
document.head.appendChild(styleTag);
|
|
138
|
+
} catch (error) {
|
|
139
|
+
console.error('Error applying theme settings:', error);
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
// Busca as settings do grupo de localization
|
|
144
|
+
const { data: localizationSettings, isLoading } = useQuery<
|
|
145
|
+
PaginatedResult<Setting>
|
|
146
|
+
>({
|
|
147
|
+
queryKey: ['setting', 'localization', currentLocaleCode],
|
|
148
|
+
queryFn: async () => {
|
|
149
|
+
const response = await request<PaginatedResult<Setting>>({
|
|
150
|
+
url: '/setting/group/localization',
|
|
151
|
+
});
|
|
152
|
+
return response.data;
|
|
153
|
+
},
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// Encontra as settings específicas
|
|
157
|
+
const dateFormatSetting = localizationSettings?.data?.find(
|
|
158
|
+
(s: Setting) => s.slug === 'date-format'
|
|
159
|
+
);
|
|
160
|
+
const timeFormatSetting = localizationSettings?.data?.find(
|
|
161
|
+
(s: Setting) => s.slug === 'time-format'
|
|
162
|
+
);
|
|
163
|
+
const timezoneSetting = localizationSettings?.data?.find(
|
|
164
|
+
(s: Setting) => s.slug === 'timezone'
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
const dateFormat = getSettingValue('date-format') || 'dd/MM/yyyy';
|
|
168
|
+
const timeFormat = getSettingValue('time-format') || 'HH:mm:ss';
|
|
169
|
+
const timezone = getSettingValue('timezone') || 'UTC';
|
|
170
|
+
|
|
171
|
+
const schema = z.object({
|
|
172
|
+
language: z.string().min(1, t('languageRequired')),
|
|
173
|
+
theme: z.string().min(1, t('themeRequired')),
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
const form = useForm({
|
|
177
|
+
resolver: zodResolver(schema),
|
|
178
|
+
defaultValues: {
|
|
179
|
+
language: '',
|
|
180
|
+
theme: '',
|
|
181
|
+
},
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
const handleThemeChange = async (value: 'light' | 'dark' | 'system') => {
|
|
185
|
+
setCurrentTheme(value);
|
|
186
|
+
await request({
|
|
187
|
+
url: '/profile/preferences',
|
|
188
|
+
method: 'PUT',
|
|
189
|
+
data: { theme: value },
|
|
190
|
+
});
|
|
191
|
+
await applyThemeSettings(value);
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
const handleLanguageChange = (code: string) => {
|
|
195
|
+
request({
|
|
196
|
+
url: '/profile/preferences',
|
|
197
|
+
method: 'PUT',
|
|
198
|
+
data: { language: code },
|
|
199
|
+
});
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
const handleDateFormatChange = async (value: string) => {
|
|
203
|
+
try {
|
|
204
|
+
await setSettingValue('date-format', value);
|
|
205
|
+
showToastHandler(
|
|
206
|
+
'success',
|
|
207
|
+
t('dateFormatUpdated') || 'Date format updated successfully'
|
|
208
|
+
);
|
|
209
|
+
} catch (error) {
|
|
210
|
+
showToastHandler(
|
|
211
|
+
'error',
|
|
212
|
+
t('dateFormatUpdateError') || 'Failed to update date format'
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
const handleTimeFormatChange = async (value: string) => {
|
|
218
|
+
try {
|
|
219
|
+
await setSettingValue('time-format', value);
|
|
220
|
+
showToastHandler(
|
|
221
|
+
'success',
|
|
222
|
+
t('timeFormatUpdated') || 'Time format updated successfully'
|
|
223
|
+
);
|
|
224
|
+
} catch (error) {
|
|
225
|
+
showToastHandler(
|
|
226
|
+
'error',
|
|
227
|
+
t('timeFormatUpdateError') || 'Failed to update time format'
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
const handleTimezoneChange = async (value: string) => {
|
|
233
|
+
try {
|
|
234
|
+
await setSettingValue('timezone', value);
|
|
235
|
+
showToastHandler(
|
|
236
|
+
'success',
|
|
237
|
+
t('timezoneUpdated') || 'Timezone updated successfully'
|
|
238
|
+
);
|
|
239
|
+
} catch (error) {
|
|
240
|
+
showToastHandler(
|
|
241
|
+
'error',
|
|
242
|
+
t('timezoneUpdateError') || 'Failed to update timezone'
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
return (
|
|
248
|
+
<div className="flex flex-col h-screen px-4">
|
|
249
|
+
<PageHeader
|
|
250
|
+
breadcrumbs={[
|
|
251
|
+
{ label: 'Home', href: '/dashboard' },
|
|
252
|
+
{ label: t('title') },
|
|
253
|
+
]}
|
|
254
|
+
title={t('title')}
|
|
255
|
+
description={t('description')}
|
|
256
|
+
/>
|
|
257
|
+
|
|
258
|
+
<div className="mt-2">
|
|
259
|
+
<Form {...form}>
|
|
260
|
+
<form className="space-y-6">
|
|
261
|
+
<h2 className="text-1xl font-bold text-primary mb-1">
|
|
262
|
+
{t('appearance')}
|
|
263
|
+
</h2>
|
|
264
|
+
<Card className="border-none bg-accent">
|
|
265
|
+
<CardContent className="flex-col gap-4">
|
|
266
|
+
<div className="flex justify-between">
|
|
267
|
+
<div className="flex-col">
|
|
268
|
+
<label className="font-semibold">{t('theme')}</label>
|
|
269
|
+
<p className="text-muted-foreground text-sm">
|
|
270
|
+
{t('setPreferredTheme')}
|
|
271
|
+
</p>
|
|
272
|
+
</div>
|
|
273
|
+
|
|
274
|
+
<div className="h-full flex items-center">
|
|
275
|
+
<Select
|
|
276
|
+
value={currentTheme}
|
|
277
|
+
onValueChange={handleThemeChange}
|
|
278
|
+
>
|
|
279
|
+
<SelectTrigger className="w-[180px] bg-background">
|
|
280
|
+
<SelectValue placeholder={t('selectTheme')} />
|
|
281
|
+
</SelectTrigger>
|
|
282
|
+
<SelectContent>
|
|
283
|
+
<SelectItem value="light">{t('light')}</SelectItem>
|
|
284
|
+
<SelectItem value="dark">{t('dark')}</SelectItem>
|
|
285
|
+
<SelectItem value="system">{t('system')}</SelectItem>
|
|
286
|
+
</SelectContent>
|
|
287
|
+
</Select>
|
|
288
|
+
</div>
|
|
289
|
+
</div>
|
|
290
|
+
</CardContent>
|
|
291
|
+
</Card>
|
|
292
|
+
|
|
293
|
+
<h2 className="text-1xl font-bold text-primary mb-1">
|
|
294
|
+
{t('localization')}
|
|
295
|
+
</h2>
|
|
296
|
+
<Card className="border-none bg-accent">
|
|
297
|
+
<CardContent className="flex-col gap-4">
|
|
298
|
+
<div className="flex justify-between">
|
|
299
|
+
<div className="flex-col">
|
|
300
|
+
<label className="font-semibold">{t('language')}</label>
|
|
301
|
+
<p className="text-muted-foreground text-sm">
|
|
302
|
+
{t('setPreferredLanguage')}
|
|
303
|
+
</p>
|
|
304
|
+
</div>
|
|
305
|
+
|
|
306
|
+
<div className="h-full flex items-center">
|
|
307
|
+
<LanguageSelector onChange={handleLanguageChange} />
|
|
308
|
+
</div>
|
|
309
|
+
</div>
|
|
310
|
+
</CardContent>
|
|
311
|
+
</Card>
|
|
312
|
+
|
|
313
|
+
<h2 className="text-1xl font-bold text-primary mb-1">
|
|
314
|
+
{t('dateAndTimeFormat')}
|
|
315
|
+
</h2>
|
|
316
|
+
<Card className="border-none bg-accent">
|
|
317
|
+
{isLoading && (
|
|
318
|
+
<CardContent className="flex h-32 w-full items-center justify-center">
|
|
319
|
+
<Loader className="animate-spin text-muted-foreground w-4 h-4" />
|
|
320
|
+
</CardContent>
|
|
321
|
+
)}
|
|
322
|
+
{!isLoading && dateFormatSetting && timeFormatSetting && (
|
|
323
|
+
<CardContent className="flex-col gap-4">
|
|
324
|
+
<div className="flex justify-between">
|
|
325
|
+
<div className="flex-col">
|
|
326
|
+
<label className="font-semibold">{t('dateFormat')}</label>
|
|
327
|
+
<p className="text-muted-foreground text-sm">
|
|
328
|
+
{t('setPreferredDateFormat')}
|
|
329
|
+
</p>
|
|
330
|
+
</div>
|
|
331
|
+
|
|
332
|
+
<div className="h-full flex items-center">
|
|
333
|
+
<Select
|
|
334
|
+
value={dateFormat}
|
|
335
|
+
onValueChange={handleDateFormatChange}
|
|
336
|
+
>
|
|
337
|
+
<SelectTrigger className="w-[180px] bg-background">
|
|
338
|
+
<SelectValue
|
|
339
|
+
placeholder={
|
|
340
|
+
t('selectDateFormat') || 'Select format'
|
|
341
|
+
}
|
|
342
|
+
/>
|
|
343
|
+
</SelectTrigger>
|
|
344
|
+
<SelectContent>
|
|
345
|
+
{(dateFormatSetting.setting_list || []).map(
|
|
346
|
+
(option: SettingList) => (
|
|
347
|
+
<SelectItem key={option.id} value={option.value}>
|
|
348
|
+
{option.value}
|
|
349
|
+
</SelectItem>
|
|
350
|
+
)
|
|
351
|
+
)}
|
|
352
|
+
</SelectContent>
|
|
353
|
+
</Select>
|
|
354
|
+
</div>
|
|
355
|
+
</div>
|
|
356
|
+
|
|
357
|
+
<div className="flex justify-between mt-4">
|
|
358
|
+
<div className="flex-col">
|
|
359
|
+
<label className="font-semibold">{t('timeFormat')}</label>
|
|
360
|
+
<p className="text-muted-foreground text-sm">
|
|
361
|
+
{t('setPreferredTimeFormat')}
|
|
362
|
+
</p>
|
|
363
|
+
</div>
|
|
364
|
+
|
|
365
|
+
<div className="h-full flex items-center">
|
|
366
|
+
<Select
|
|
367
|
+
value={timeFormat}
|
|
368
|
+
onValueChange={handleTimeFormatChange}
|
|
369
|
+
>
|
|
370
|
+
<SelectTrigger className="w-[180px] bg-background">
|
|
371
|
+
<SelectValue
|
|
372
|
+
placeholder={
|
|
373
|
+
t('selectTimeFormat') || 'Select format'
|
|
374
|
+
}
|
|
375
|
+
/>
|
|
376
|
+
</SelectTrigger>
|
|
377
|
+
<SelectContent>
|
|
378
|
+
{(timeFormatSetting.setting_list || []).map(
|
|
379
|
+
(option: SettingList) => (
|
|
380
|
+
<SelectItem key={option.id} value={option.value}>
|
|
381
|
+
{option.value}
|
|
382
|
+
</SelectItem>
|
|
383
|
+
)
|
|
384
|
+
)}
|
|
385
|
+
</SelectContent>
|
|
386
|
+
</Select>
|
|
387
|
+
</div>
|
|
388
|
+
</div>
|
|
389
|
+
</CardContent>
|
|
390
|
+
)}
|
|
391
|
+
</Card>
|
|
392
|
+
|
|
393
|
+
<h2 className="text-1xl font-bold text-primary mb-1">
|
|
394
|
+
{t('timezone')}
|
|
395
|
+
</h2>
|
|
396
|
+
<Card className="border-none bg-accent">
|
|
397
|
+
{isLoading && (
|
|
398
|
+
<CardContent className="flex h-32 w-full items-center justify-center">
|
|
399
|
+
<Loader className="animate-spin text-muted-foreground w-4 h-4" />
|
|
400
|
+
</CardContent>
|
|
401
|
+
)}
|
|
402
|
+
{!isLoading && timezoneSetting && (
|
|
403
|
+
<CardContent className="flex-col gap-4">
|
|
404
|
+
<div className="flex justify-between">
|
|
405
|
+
<div className="flex-col">
|
|
406
|
+
<label className="font-semibold">
|
|
407
|
+
{timezoneSetting.name || t('timezone')}
|
|
408
|
+
</label>
|
|
409
|
+
<p className="text-muted-foreground text-sm">
|
|
410
|
+
{timezoneSetting.description ||
|
|
411
|
+
t('setPreferredTimezone')}
|
|
412
|
+
</p>
|
|
413
|
+
</div>
|
|
414
|
+
|
|
415
|
+
<div className="h-full flex items-center">
|
|
416
|
+
<Select
|
|
417
|
+
key={timezone}
|
|
418
|
+
value={timezone}
|
|
419
|
+
onValueChange={handleTimezoneChange}
|
|
420
|
+
>
|
|
421
|
+
<SelectTrigger className="w-60 bg-background">
|
|
422
|
+
<SelectValue
|
|
423
|
+
placeholder={
|
|
424
|
+
t('selectTimezone') || 'Select timezone'
|
|
425
|
+
}
|
|
426
|
+
/>
|
|
427
|
+
</SelectTrigger>
|
|
428
|
+
<SelectContent>
|
|
429
|
+
{(timezoneSetting.setting_list || []).map(
|
|
430
|
+
(option: SettingList) => (
|
|
431
|
+
<SelectItem key={option.id} value={option.value}>
|
|
432
|
+
{option.value}
|
|
433
|
+
</SelectItem>
|
|
434
|
+
)
|
|
435
|
+
)}
|
|
436
|
+
</SelectContent>
|
|
437
|
+
</Select>
|
|
438
|
+
</div>
|
|
439
|
+
</div>
|
|
440
|
+
</CardContent>
|
|
441
|
+
)}
|
|
442
|
+
</Card>
|
|
443
|
+
</form>
|
|
444
|
+
</Form>
|
|
445
|
+
</div>
|
|
446
|
+
</div>
|
|
447
|
+
);
|
|
448
|
+
}
|