uibee 2.8.3 → 2.8.6
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/dist/src/components/inputs/input.js +24 -2
- package/dist/src/components/inputs/select.d.ts +2 -1
- package/dist/src/components/inputs/select.js +21 -10
- package/dist/src/components/inputs/shared/dateTimePickerPopup.js +3 -2
- package/dist/src/components/login/loginPage.d.ts +1 -1
- package/dist/src/components/login/loginPage.js +3 -2
- package/dist/src/globals.css +16 -0
- package/package.json +1 -1
- package/src/components/inputs/input.tsx +28 -2
- package/src/components/inputs/select.tsx +67 -33
- package/src/components/inputs/shared/dateTimePickerPopup.tsx +4 -2
- package/src/components/login/loginPage.tsx +18 -1
- package/src/types/components.d.ts +2 -0
|
@@ -83,10 +83,32 @@ export default function Input(props) {
|
|
|
83
83
|
const date = new Date(value);
|
|
84
84
|
return isNaN(date.getTime()) ? null : date;
|
|
85
85
|
}
|
|
86
|
+
function getDateDisplayValue() {
|
|
87
|
+
if (!value || !isDateType)
|
|
88
|
+
return value;
|
|
89
|
+
const date = getDateValue();
|
|
90
|
+
if (!date)
|
|
91
|
+
return value;
|
|
92
|
+
function pad(n) {
|
|
93
|
+
return n.toString().padStart(2, '0');
|
|
94
|
+
}
|
|
95
|
+
const yyyy = date.getFullYear();
|
|
96
|
+
const MM = pad(date.getMonth() + 1);
|
|
97
|
+
const dd = pad(date.getDate());
|
|
98
|
+
const hh = pad(date.getHours());
|
|
99
|
+
const mm = pad(date.getMinutes());
|
|
100
|
+
if (type === 'date')
|
|
101
|
+
return `${dd}.${MM}.${yyyy}`;
|
|
102
|
+
if (type === 'time')
|
|
103
|
+
return `${hh}:${mm}`;
|
|
104
|
+
if (type === 'datetime-local')
|
|
105
|
+
return `${dd}.${MM}.${yyyy} ${hh}:${mm}`;
|
|
106
|
+
return value;
|
|
107
|
+
}
|
|
86
108
|
return (_jsx(FieldWrapper, { label: label, name: name, required: inputProps.required, info: info, error: error, className: className, children: _jsxs("div", { className: 'relative flex items-center', ref: containerRef, children: [displayIcon && (_jsx("div", { className: `
|
|
87
109
|
absolute left-3 text-login-200
|
|
88
110
|
${isClickableType && !inputProps.disabled ? 'cursor-pointer hover:text-login-text' : 'pointer-events-none'}
|
|
89
|
-
`, onClick: handleIconClick, children: displayIcon })), _jsx("input", { ...inputProps, ref: localRef, id: name, name: name, type: isClickableType ? 'text' : type, value: value, readOnly: isClickableType, onClick: () => isClickableType && !inputProps.disabled && setIsOpen(true), title: label, "aria-invalid": !!error, "aria-describedby": error ? `${name}-error` : undefined, className: `
|
|
111
|
+
`, onClick: handleIconClick, children: displayIcon })), _jsx("input", { ...inputProps, ref: localRef, id: name, name: isClickableType ? undefined : name, type: isClickableType ? 'text' : type, value: isDateType ? getDateDisplayValue() : value, readOnly: isClickableType, onClick: () => isClickableType && !inputProps.disabled && setIsOpen(true), title: label, "aria-invalid": !!error, "aria-describedby": error ? `${name}-error` : undefined, className: `
|
|
90
112
|
w-full rounded-md bg-login-500/50 border border-login-500
|
|
91
113
|
text-login-text placeholder-login-200
|
|
92
114
|
focus:outline-none focus:border-login focus:ring-1 focus:ring-login
|
|
@@ -96,5 +118,5 @@ export default function Input(props) {
|
|
|
96
118
|
input-reset
|
|
97
119
|
${error ? 'border-red-500 focus:border-red-500 focus:ring-red-500' : ''}
|
|
98
120
|
${isClickableType && !inputProps.disabled ? 'cursor-pointer' : ''}
|
|
99
|
-
` }), isOpen && isDateType && !inputProps.disabled && (_jsx(DateTimePickerPopup, { value: getDateValue(), onChange: handleDateChange, type: type, onClose: () => setIsOpen(false) })), isOpen && isColorType && !inputProps.disabled && (_jsx(ColorPickerPopup, { value: value || '', onChange: handleColorChange, onClose: () => setIsOpen(false) }))] }) }));
|
|
121
|
+
` }), isClickableType && (_jsx("input", { type: 'hidden', name: name, value: value })), isOpen && isDateType && !inputProps.disabled && (_jsx(DateTimePickerPopup, { value: getDateValue(), onChange: handleDateChange, type: type, onClose: () => setIsOpen(false) })), isOpen && isColorType && !inputProps.disabled && (_jsx(ColorPickerPopup, { value: value || '', onChange: handleColorChange, onClose: () => setIsOpen(false) }))] }) }));
|
|
100
122
|
}
|
|
@@ -16,5 +16,6 @@ export type SelectProps = {
|
|
|
16
16
|
placeholder?: string;
|
|
17
17
|
info?: string;
|
|
18
18
|
clearable?: boolean;
|
|
19
|
+
searchable?: boolean;
|
|
19
20
|
};
|
|
20
|
-
export default function Select({ label, name, value, onChange, options, error, className, disabled, required, placeholder, info, clearable, }: SelectProps): import("react/jsx-runtime").JSX.Element;
|
|
21
|
+
export default function Select({ label, name, value, onChange, options, error, className, disabled, required, placeholder, info, clearable, searchable, }: SelectProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -3,11 +3,17 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { useState, useEffect } from 'react';
|
|
4
4
|
import Image from 'next/image';
|
|
5
5
|
import { useClickOutside } from '../../hooks';
|
|
6
|
-
import { ChevronDown, X } from 'lucide-react';
|
|
6
|
+
import { ChevronDown, X, Search } from 'lucide-react';
|
|
7
7
|
import { FieldWrapper } from './shared';
|
|
8
|
-
export default function Select({ label, name, value, onChange, options, error, className, disabled, required, placeholder = 'Select an option', info, clearable = true, }) {
|
|
8
|
+
export default function Select({ label, name, value, onChange, options, error, className, disabled, required, placeholder = 'Select an option', info, clearable = true, searchable = true, }) {
|
|
9
9
|
const [isOpen, setIsOpen] = useState(false);
|
|
10
|
+
const [searchTerm, setSearchTerm] = useState('');
|
|
10
11
|
const [selectedOption, setSelectedOption] = useState(options.find(opt => opt.value === value));
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
if (!isOpen) {
|
|
14
|
+
setSearchTerm('');
|
|
15
|
+
}
|
|
16
|
+
}, [isOpen]);
|
|
11
17
|
useEffect(() => {
|
|
12
18
|
setSelectedOption(options.find(opt => opt.value === value));
|
|
13
19
|
}, [value, options]);
|
|
@@ -30,6 +36,7 @@ export default function Select({ label, name, value, onChange, options, error, c
|
|
|
30
36
|
onChange(null);
|
|
31
37
|
}
|
|
32
38
|
}
|
|
39
|
+
const filteredOptions = options.filter(option => option.label.toLowerCase().includes(searchTerm.toLowerCase()));
|
|
33
40
|
return (_jsxs(FieldWrapper, { label: label, name: name, required: required, info: info, error: error, className: className, children: [_jsxs("div", { className: 'relative', ref: containerRef, children: [_jsxs("button", { type: 'button', onClick: () => !disabled && setIsOpen(!isOpen), disabled: disabled, "aria-haspopup": 'listbox', "aria-expanded": isOpen, "aria-labelledby": label ? undefined : name, className: `
|
|
34
41
|
w-full rounded-md bg-login-500/50 border border-login-500
|
|
35
42
|
text-login-text text-left
|
|
@@ -47,13 +54,17 @@ export default function Select({ label, name, value, onChange, options, error, c
|
|
|
47
54
|
text-login-200 pointer-events-none
|
|
48
55
|
transition-transform duration-200
|
|
49
56
|
${isOpen ? 'rotate-180' : ''}
|
|
50
|
-
`, children: _jsx(ChevronDown, { className: 'w-4 h-4' }) })] })] }), isOpen && (
|
|
57
|
+
`, children: _jsx(ChevronDown, { className: 'w-4 h-4' }) })] })] }), isOpen && (_jsxs("div", { className: `
|
|
51
58
|
absolute z-50 w-full mt-1 bg-login-600 border border-login-500
|
|
52
|
-
rounded-md shadow-lg max-h-60 overflow-
|
|
53
|
-
`, children:
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
+
rounded-md shadow-lg max-h-60 overflow-hidden flex flex-col
|
|
60
|
+
`, children: [searchable && (_jsx("div", { className: 'p-2 sticky top-0 bg-login-600 border-b border-login-500 z-10', children: _jsxs("div", { className: 'relative', children: [_jsx(Search, { className: 'absolute left-2.5 top-1/2 -translate-y-1/2 w-4 h-4 text-login-200' }), _jsx("input", { type: 'text', value: searchTerm, onChange: (e) => setSearchTerm(e.target.value), placeholder: 'Search...', autoFocus: true, className: `
|
|
61
|
+
w-full bg-login-500/50 border border-login-500 rounded-md
|
|
62
|
+
py-1.5 pl-9 pr-3 text-sm text-login-text
|
|
63
|
+
focus:outline-none focus:border-login focus:ring-1 focus:ring-login
|
|
64
|
+
` })] }) })), _jsx("div", { className: 'overflow-auto noscroll', children: filteredOptions.length > 0 ? (_jsx("ul", { className: 'py-1', role: 'listbox', children: filteredOptions.map((option) => (_jsx("li", { role: 'option', "aria-selected": selectedOption?.value === option.value, children: _jsxs("button", { type: 'button', onClick: () => handleSelect(option), className: `
|
|
65
|
+
w-full text-left px-3 py-2 text-sm
|
|
66
|
+
hover:bg-login-500 transition-colors duration-150
|
|
67
|
+
flex items-center gap-2
|
|
68
|
+
${selectedOption?.value === option.value ? 'bg-login-500 text-login' : 'text-login-text'}
|
|
69
|
+
`, children: [option.image && (_jsx(Image, { src: option.image, alt: '', width: 75, height: 25, className: 'rounded-md object-cover shrink-0' })), _jsx("span", { className: 'truncate', children: option.label })] }) }, option.value))) })) : (_jsx("div", { className: 'px-3 py-2 text-sm text-login-200', children: searchTerm ? 'No results found' : 'No options available' })) })] }))] }), _jsx("input", { type: 'hidden', name: name, value: selectedOption?.value || '', required: required })] }));
|
|
59
70
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useState, useEffect } from 'react';
|
|
3
3
|
import { ChevronLeft, ChevronRight } from 'lucide-react';
|
|
4
|
-
const DAYS = ['
|
|
4
|
+
const DAYS = ['Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su'];
|
|
5
5
|
const MONTHS = [
|
|
6
6
|
'January', 'February', 'March', 'April', 'May', 'June',
|
|
7
7
|
'July', 'August', 'September', 'October', 'November', 'December'
|
|
@@ -87,7 +87,8 @@ export default function DateTimePickerPopup({ value, onChange, type, onClose, })
|
|
|
87
87
|
const daysInMonth = getDaysInMonth(year, month);
|
|
88
88
|
const firstDay = getFirstDayOfMonth(year, month);
|
|
89
89
|
const days = [];
|
|
90
|
-
|
|
90
|
+
const adjustedFirstDay = firstDay === 0 ? 6 : firstDay - 1;
|
|
91
|
+
for (let i = 0; i < adjustedFirstDay; i++) {
|
|
91
92
|
days.push(_jsx("div", { className: 'w-8 h-8' }, `empty-${i}`));
|
|
92
93
|
}
|
|
93
94
|
for (let i = 1; i <= daysInMonth; i++) {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { LoginPageProps } from 'uibee/components';
|
|
2
|
-
export default function LoginPage({ title, description, redirectURL, version, btg, handleSubmit }: LoginPageProps): import("react/jsx-runtime").JSX.Element;
|
|
2
|
+
export default function LoginPage({ title, description, redirectURL, version, btg, handleSubmit, guestRedirectURL, guestText }: LoginPageProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { LogIn } from 'lucide-react';
|
|
3
3
|
import Logo from '../logo/logo';
|
|
4
4
|
import Link from 'next/link';
|
|
5
|
-
export default function LoginPage({ title, description, redirectURL, version, btg, handleSubmit }) {
|
|
5
|
+
export default function LoginPage({ title, description, redirectURL, version, btg, handleSubmit, guestRedirectURL, guestText }) {
|
|
6
6
|
return (_jsx("main", { className: 'w-full h-full flex items-center justify-center bg-login-800 p-8', children: _jsxs("div", { className: 'flex flex-col justify-center items-center bg-login-600 px-4 py-12 rounded-xl w-full max-w-md gap-4 md:gap-6', children: [_jsx("div", { className: 'relative aspect-3/1 w-full', children: _jsx(Logo, { className: 'object-contain px-6 sm:px-12' }) }), _jsxs("h1", { className: 'text-3xl font-extrabold text-login text-center tracking-tight', children: [title, " ", btg ? ' - Break the Glass' : ''] }), description && (_jsx("p", { className: 'text-center font-medium text-lg mb-2 max-w-xs', children: description })), btg ? (_jsxs("form", { className: 'w-full flex flex-col gap-3 max-w-xs', onSubmit: e => {
|
|
7
7
|
e.preventDefault();
|
|
8
8
|
handleSubmit?.(new FormData(e.currentTarget));
|
|
@@ -13,5 +13,6 @@ export default function LoginPage({ title, description, redirectURL, version, bt
|
|
|
13
13
|
max-w-xs py-3 px-6 rounded-xl bg-login font-bold
|
|
14
14
|
text-lg hover:bg-login/80 transition-all
|
|
15
15
|
duration-200 mb-2 mt-2 cursor-pointer
|
|
16
|
-
`, children: ["Login", _jsx(LogIn, { className: 'w-6 h-6' })] })),
|
|
16
|
+
`, children: ["Login", _jsx(LogIn, { className: 'w-6 h-6' })] })), guestRedirectURL &&
|
|
17
|
+
_jsx(Link, { href: guestRedirectURL, className: 'text-sm font-semibold cursor-pointer opacity-50', children: guestText || 'Continue as guest' }), _jsxs("span", { className: 'text-sm mt-2', children: ["v", version] })] }) }));
|
|
17
18
|
}
|
package/dist/src/globals.css
CHANGED
|
@@ -296,6 +296,9 @@
|
|
|
296
296
|
.relative {
|
|
297
297
|
position: relative;
|
|
298
298
|
}
|
|
299
|
+
.sticky {
|
|
300
|
+
position: sticky;
|
|
301
|
+
}
|
|
299
302
|
.inset-0 {
|
|
300
303
|
inset: calc(var(--spacing) * 0);
|
|
301
304
|
}
|
|
@@ -350,6 +353,9 @@
|
|
|
350
353
|
.left-2 {
|
|
351
354
|
left: calc(var(--spacing) * 2);
|
|
352
355
|
}
|
|
356
|
+
.left-2\.5 {
|
|
357
|
+
left: calc(var(--spacing) * 2.5);
|
|
358
|
+
}
|
|
353
359
|
.left-3 {
|
|
354
360
|
left: calc(var(--spacing) * 3);
|
|
355
361
|
}
|
|
@@ -1416,6 +1422,10 @@
|
|
|
1416
1422
|
border-top-style: var(--tw-border-style);
|
|
1417
1423
|
border-top-width: 1px;
|
|
1418
1424
|
}
|
|
1425
|
+
.border-b {
|
|
1426
|
+
border-bottom-style: var(--tw-border-style);
|
|
1427
|
+
border-bottom-width: 1px;
|
|
1428
|
+
}
|
|
1419
1429
|
.border-none {
|
|
1420
1430
|
--tw-border-style: none;
|
|
1421
1431
|
border-style: none;
|
|
@@ -1608,6 +1618,9 @@
|
|
|
1608
1618
|
.py-1 {
|
|
1609
1619
|
padding-block: calc(var(--spacing) * 1);
|
|
1610
1620
|
}
|
|
1621
|
+
.py-1\.5 {
|
|
1622
|
+
padding-block: calc(var(--spacing) * 1.5);
|
|
1623
|
+
}
|
|
1611
1624
|
.py-2 {
|
|
1612
1625
|
padding-block: calc(var(--spacing) * 2);
|
|
1613
1626
|
}
|
|
@@ -1647,6 +1660,9 @@
|
|
|
1647
1660
|
.pl-4 {
|
|
1648
1661
|
padding-left: calc(var(--spacing) * 4);
|
|
1649
1662
|
}
|
|
1663
|
+
.pl-9 {
|
|
1664
|
+
padding-left: calc(var(--spacing) * 9);
|
|
1665
|
+
}
|
|
1650
1666
|
.pl-10 {
|
|
1651
1667
|
padding-left: calc(var(--spacing) * 10);
|
|
1652
1668
|
}
|
package/package.json
CHANGED
|
@@ -100,6 +100,29 @@ export default function Input(props: InputProps) {
|
|
|
100
100
|
return isNaN(date.getTime()) ? null : date
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
+
function getDateDisplayValue() {
|
|
104
|
+
if (!value || !isDateType) return value as string
|
|
105
|
+
|
|
106
|
+
const date = getDateValue()
|
|
107
|
+
if (!date) return value as string
|
|
108
|
+
|
|
109
|
+
function pad(n: number) {
|
|
110
|
+
return n.toString().padStart(2, '0')
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const yyyy = date.getFullYear()
|
|
114
|
+
const MM = pad(date.getMonth() + 1)
|
|
115
|
+
const dd = pad(date.getDate())
|
|
116
|
+
const hh = pad(date.getHours())
|
|
117
|
+
const mm = pad(date.getMinutes())
|
|
118
|
+
|
|
119
|
+
if (type === 'date') return `${dd}.${MM}.${yyyy}`
|
|
120
|
+
if (type === 'time') return `${hh}:${mm}`
|
|
121
|
+
if (type === 'datetime-local') return `${dd}.${MM}.${yyyy} ${hh}:${mm}`
|
|
122
|
+
|
|
123
|
+
return value as string
|
|
124
|
+
}
|
|
125
|
+
|
|
103
126
|
return (
|
|
104
127
|
<FieldWrapper
|
|
105
128
|
label={label}
|
|
@@ -125,9 +148,9 @@ export default function Input(props: InputProps) {
|
|
|
125
148
|
{...inputProps}
|
|
126
149
|
ref={localRef}
|
|
127
150
|
id={name}
|
|
128
|
-
name={name}
|
|
151
|
+
name={isClickableType ? undefined : name}
|
|
129
152
|
type={isClickableType ? 'text' : type}
|
|
130
|
-
value={value}
|
|
153
|
+
value={isDateType ? getDateDisplayValue() : value}
|
|
131
154
|
readOnly={isClickableType}
|
|
132
155
|
onClick={() => isClickableType && !inputProps.disabled && setIsOpen(true)}
|
|
133
156
|
title={label}
|
|
@@ -145,6 +168,9 @@ export default function Input(props: InputProps) {
|
|
|
145
168
|
${isClickableType && !inputProps.disabled ? 'cursor-pointer' : ''}
|
|
146
169
|
`}
|
|
147
170
|
/>
|
|
171
|
+
{isClickableType && (
|
|
172
|
+
<input type='hidden' name={name} value={value as string} />
|
|
173
|
+
)}
|
|
148
174
|
{isOpen && isDateType && !inputProps.disabled && (
|
|
149
175
|
<DateTimePickerPopup
|
|
150
176
|
value={getDateValue()}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { useState, useEffect } from 'react'
|
|
4
4
|
import Image from 'next/image'
|
|
5
5
|
import { useClickOutside } from '../../hooks'
|
|
6
|
-
import { ChevronDown, X } from 'lucide-react'
|
|
6
|
+
import { ChevronDown, X, Search } from 'lucide-react'
|
|
7
7
|
import { FieldWrapper } from './shared'
|
|
8
8
|
|
|
9
9
|
export type Option = {
|
|
@@ -25,6 +25,7 @@ export type SelectProps = {
|
|
|
25
25
|
placeholder?: string
|
|
26
26
|
info?: string
|
|
27
27
|
clearable?: boolean
|
|
28
|
+
searchable?: boolean
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
export default function Select({
|
|
@@ -40,12 +41,20 @@ export default function Select({
|
|
|
40
41
|
placeholder = 'Select an option',
|
|
41
42
|
info,
|
|
42
43
|
clearable = true,
|
|
44
|
+
searchable = true,
|
|
43
45
|
}: SelectProps) {
|
|
44
46
|
const [isOpen, setIsOpen] = useState(false)
|
|
47
|
+
const [searchTerm, setSearchTerm] = useState('')
|
|
45
48
|
const [selectedOption, setSelectedOption] = useState<Option | undefined>(
|
|
46
49
|
options.find(opt => opt.value === value)
|
|
47
50
|
)
|
|
48
51
|
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
if (!isOpen) {
|
|
54
|
+
setSearchTerm('')
|
|
55
|
+
}
|
|
56
|
+
}, [isOpen])
|
|
57
|
+
|
|
49
58
|
useEffect(() => {
|
|
50
59
|
setSelectedOption(options.find(opt => opt.value === value))
|
|
51
60
|
}, [value, options])
|
|
@@ -70,6 +79,10 @@ export default function Select({
|
|
|
70
79
|
}
|
|
71
80
|
}
|
|
72
81
|
|
|
82
|
+
const filteredOptions = options.filter(option =>
|
|
83
|
+
option.label.toLowerCase().includes(searchTerm.toLowerCase())
|
|
84
|
+
)
|
|
85
|
+
|
|
73
86
|
return (
|
|
74
87
|
<FieldWrapper
|
|
75
88
|
label={label}
|
|
@@ -141,41 +154,62 @@ export default function Select({
|
|
|
141
154
|
{isOpen && (
|
|
142
155
|
<div className={`
|
|
143
156
|
absolute z-50 w-full mt-1 bg-login-600 border border-login-500
|
|
144
|
-
rounded-md shadow-lg max-h-60 overflow-
|
|
157
|
+
rounded-md shadow-lg max-h-60 overflow-hidden flex flex-col
|
|
145
158
|
`}>
|
|
146
|
-
{
|
|
147
|
-
<
|
|
148
|
-
|
|
149
|
-
<
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
alt=''
|
|
164
|
-
width={75}
|
|
165
|
-
height={25}
|
|
166
|
-
className='rounded-md object-cover shrink-0'
|
|
167
|
-
/>
|
|
168
|
-
)}
|
|
169
|
-
<span className='truncate'>{option.label}</span>
|
|
170
|
-
</button>
|
|
171
|
-
</li>
|
|
172
|
-
))}
|
|
173
|
-
</ul>
|
|
174
|
-
) : (
|
|
175
|
-
<div className='px-3 py-2 text-sm text-login-200'>
|
|
176
|
-
No options available
|
|
159
|
+
{searchable && (
|
|
160
|
+
<div className='p-2 sticky top-0 bg-login-600 border-b border-login-500 z-10'>
|
|
161
|
+
<div className='relative'>
|
|
162
|
+
<Search className='absolute left-2.5 top-1/2 -translate-y-1/2 w-4 h-4 text-login-200' />
|
|
163
|
+
<input
|
|
164
|
+
type='text'
|
|
165
|
+
value={searchTerm}
|
|
166
|
+
onChange={(e) => setSearchTerm(e.target.value)}
|
|
167
|
+
placeholder='Search...'
|
|
168
|
+
autoFocus
|
|
169
|
+
className={`
|
|
170
|
+
w-full bg-login-500/50 border border-login-500 rounded-md
|
|
171
|
+
py-1.5 pl-9 pr-3 text-sm text-login-text
|
|
172
|
+
focus:outline-none focus:border-login focus:ring-1 focus:ring-login
|
|
173
|
+
`}
|
|
174
|
+
/>
|
|
175
|
+
</div>
|
|
177
176
|
</div>
|
|
178
177
|
)}
|
|
178
|
+
<div className='overflow-auto noscroll'>
|
|
179
|
+
{filteredOptions.length > 0 ? (
|
|
180
|
+
<ul className='py-1' role='listbox'>
|
|
181
|
+
{filteredOptions.map((option) => (
|
|
182
|
+
<li key={option.value} role='option' aria-selected={selectedOption?.value === option.value}>
|
|
183
|
+
<button
|
|
184
|
+
type='button'
|
|
185
|
+
onClick={() => handleSelect(option)}
|
|
186
|
+
className={`
|
|
187
|
+
w-full text-left px-3 py-2 text-sm
|
|
188
|
+
hover:bg-login-500 transition-colors duration-150
|
|
189
|
+
flex items-center gap-2
|
|
190
|
+
${selectedOption?.value === option.value ? 'bg-login-500 text-login': 'text-login-text'}
|
|
191
|
+
`}
|
|
192
|
+
>
|
|
193
|
+
{option.image && (
|
|
194
|
+
<Image
|
|
195
|
+
src={option.image}
|
|
196
|
+
alt=''
|
|
197
|
+
width={75}
|
|
198
|
+
height={25}
|
|
199
|
+
className='rounded-md object-cover shrink-0'
|
|
200
|
+
/>
|
|
201
|
+
)}
|
|
202
|
+
<span className='truncate'>{option.label}</span>
|
|
203
|
+
</button>
|
|
204
|
+
</li>
|
|
205
|
+
))}
|
|
206
|
+
</ul>
|
|
207
|
+
) : (
|
|
208
|
+
<div className='px-3 py-2 text-sm text-login-200'>
|
|
209
|
+
{searchTerm ? 'No results found' : 'No options available'}
|
|
210
|
+
</div>
|
|
211
|
+
)}
|
|
212
|
+
</div>
|
|
179
213
|
</div>
|
|
180
214
|
)}
|
|
181
215
|
</div>
|
|
@@ -8,7 +8,7 @@ type DateTimePickerPopupProps = {
|
|
|
8
8
|
onClose?: () => void
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
const DAYS = ['
|
|
11
|
+
const DAYS = ['Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su']
|
|
12
12
|
const MONTHS = [
|
|
13
13
|
'January', 'February', 'March', 'April', 'May', 'June',
|
|
14
14
|
'July', 'August', 'September', 'October', 'November', 'December'
|
|
@@ -112,7 +112,9 @@ export default function DateTimePickerPopup({
|
|
|
112
112
|
const firstDay = getFirstDayOfMonth(year, month)
|
|
113
113
|
const days = []
|
|
114
114
|
|
|
115
|
-
|
|
115
|
+
const adjustedFirstDay = firstDay === 0 ? 6 : firstDay - 1
|
|
116
|
+
|
|
117
|
+
for (let i = 0; i < adjustedFirstDay; i++) {
|
|
116
118
|
days.push(<div key={`empty-${i}`} className='w-8 h-8' />)
|
|
117
119
|
}
|
|
118
120
|
|
|
@@ -3,7 +3,16 @@ import { LogIn } from 'lucide-react'
|
|
|
3
3
|
import Logo from '@components/logo/logo'
|
|
4
4
|
import Link from 'next/link'
|
|
5
5
|
|
|
6
|
-
export default function LoginPage({
|
|
6
|
+
export default function LoginPage({
|
|
7
|
+
title,
|
|
8
|
+
description,
|
|
9
|
+
redirectURL,
|
|
10
|
+
version,
|
|
11
|
+
btg,
|
|
12
|
+
handleSubmit,
|
|
13
|
+
guestRedirectURL,
|
|
14
|
+
guestText
|
|
15
|
+
}: LoginPageProps) {
|
|
7
16
|
return (
|
|
8
17
|
<main className='w-full h-full flex items-center justify-center bg-login-800 p-8'>
|
|
9
18
|
<div
|
|
@@ -69,6 +78,14 @@ export default function LoginPage({ title, description, redirectURL, version, bt
|
|
|
69
78
|
<LogIn className='w-6 h-6' />
|
|
70
79
|
</Link>
|
|
71
80
|
)}
|
|
81
|
+
{guestRedirectURL &&
|
|
82
|
+
<Link
|
|
83
|
+
href={guestRedirectURL}
|
|
84
|
+
className='text-sm font-semibold cursor-pointer opacity-50'
|
|
85
|
+
>
|
|
86
|
+
{guestText || 'Continue as guest'}
|
|
87
|
+
</Link>
|
|
88
|
+
}
|
|
72
89
|
<span className='text-sm mt-2'>v{version}</span>
|
|
73
90
|
</div>
|
|
74
91
|
</main>
|