@licklist/design 0.78.5-dev.54 → 0.78.5-dev.56

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.
Files changed (73) hide show
  1. package/dist/index.js +2 -7
  2. package/dist/v2/components/ActionMenu/ActionMenu.d.ts +1 -0
  3. package/dist/v2/components/ActionMenu/ActionMenu.d.ts.map +1 -1
  4. package/dist/v2/components/ActionMenu/ActionMenu.js +10 -3
  5. package/dist/v2/components/ActionMenu/ActionMenu.scss.js +1 -1
  6. package/dist/v2/components/Button/Button.d.ts +3 -2
  7. package/dist/v2/components/Button/Button.d.ts.map +1 -1
  8. package/dist/v2/components/Button/Button.js +12 -6
  9. package/dist/v2/components/Button/Button.scss.js +1 -1
  10. package/dist/v2/components/NewPageHeader/NewPageHeader.d.ts +2 -1
  11. package/dist/v2/components/NewPageHeader/NewPageHeader.d.ts.map +1 -1
  12. package/dist/v2/components/NewPageHeader/NewPageHeader.js +2 -2
  13. package/dist/v2/components/NewTable/NewTable.d.ts +1 -0
  14. package/dist/v2/components/NewTable/NewTable.d.ts.map +1 -1
  15. package/dist/v2/components/NewTable/NewTable.js +5 -0
  16. package/dist/v2/components/Pagination/Pagination.scss.js +1 -1
  17. package/dist/v2/components/index.d.ts +3 -10
  18. package/dist/v2/components/index.d.ts.map +1 -1
  19. package/dist/v2/icons/index.d.ts +18 -3
  20. package/dist/v2/icons/index.d.ts.map +1 -1
  21. package/dist/v2/icons/index.js +150 -7
  22. package/dist/v2/pages/Settings/components/SidebarCustomisation.js +7 -11
  23. package/dist/v2/pages/Settings/components/SidebarNavItem.js +6 -11
  24. package/dist/v2/styles/components/Button.scss +34 -2
  25. package/package.json +1 -1
  26. package/src/v2/components/ActionMenu/ActionMenu.scss +16 -5
  27. package/src/v2/components/ActionMenu/ActionMenu.tsx +6 -3
  28. package/src/v2/components/Button/Button.tsx +6 -3
  29. package/src/v2/components/Customer/{CustomerDetail/CustomerDetail.scss → CustomerDetail.scss} +7 -3
  30. package/src/v2/components/Customer/CustomersList.scss +308 -36
  31. package/src/v2/components/NewPageHeader/NewPageHeader.tsx +6 -4
  32. package/src/v2/components/NewTable/NewTable.tsx +6 -0
  33. package/src/v2/components/Pagination/Pagination.scss +24 -24
  34. package/src/v2/components/index.ts +19 -13
  35. package/src/v2/icons/index.tsx +35 -6
  36. package/src/v2/styles/components/Button.scss +34 -2
  37. package/dist/v2/components/Customer/CustomerCreate/CustomerCreate.d.ts +0 -11
  38. package/dist/v2/components/Customer/CustomerCreate/CustomerCreate.d.ts.map +0 -1
  39. package/dist/v2/components/Customer/CustomerCreate/CustomerCreate.js +0 -32
  40. package/dist/v2/components/Customer/CustomerCreate/index.d.ts +0 -2
  41. package/dist/v2/components/Customer/CustomerCreate/index.d.ts.map +0 -1
  42. package/dist/v2/components/Customer/CustomerDetail/CustomerDetail.d.ts +0 -35
  43. package/dist/v2/components/Customer/CustomerDetail/CustomerDetail.d.ts.map +0 -1
  44. package/dist/v2/components/Customer/CustomerDetail/CustomerDetail.js +0 -235
  45. package/dist/v2/components/Customer/CustomerDetail/CustomerDetail.scss.js +0 -6
  46. package/dist/v2/components/Customer/CustomerDetail/index.d.ts +0 -2
  47. package/dist/v2/components/Customer/CustomerDetail/index.d.ts.map +0 -1
  48. package/dist/v2/components/Customer/CustomerEdit/CustomerEdit.d.ts +0 -11
  49. package/dist/v2/components/Customer/CustomerEdit/CustomerEdit.d.ts.map +0 -1
  50. package/dist/v2/components/Customer/CustomerEdit/CustomerEdit.js +0 -32
  51. package/dist/v2/components/Customer/CustomerEdit/index.d.ts +0 -2
  52. package/dist/v2/components/Customer/CustomerEdit/index.d.ts.map +0 -1
  53. package/dist/v2/components/Customer/CustomerForm/CustomerForm.d.ts +0 -22
  54. package/dist/v2/components/Customer/CustomerForm/CustomerForm.d.ts.map +0 -1
  55. package/dist/v2/components/Customer/CustomerForm/CustomerForm.js +0 -535
  56. package/dist/v2/components/Customer/CustomerForm/index.d.ts +0 -2
  57. package/dist/v2/components/Customer/CustomerForm/index.d.ts.map +0 -1
  58. package/dist/v2/components/Customer/CustomersList.d.ts +0 -37
  59. package/dist/v2/components/Customer/CustomersList.d.ts.map +0 -1
  60. package/dist/v2/components/Customer/CustomersList.js +0 -204
  61. package/dist/v2/components/Customer/CustomersList.scss.js +0 -6
  62. package/dist/v2/components/Customer/index.d.ts +0 -6
  63. package/dist/v2/components/Customer/index.d.ts.map +0 -1
  64. package/src/v2/components/Customer/CustomerCreate/CustomerCreate.tsx +0 -36
  65. package/src/v2/components/Customer/CustomerCreate/index.ts +0 -1
  66. package/src/v2/components/Customer/CustomerDetail/CustomerDetail.tsx +0 -161
  67. package/src/v2/components/Customer/CustomerDetail/index.ts +0 -1
  68. package/src/v2/components/Customer/CustomerEdit/CustomerEdit.tsx +0 -37
  69. package/src/v2/components/Customer/CustomerEdit/index.ts +0 -1
  70. package/src/v2/components/Customer/CustomerForm/CustomerForm.tsx +0 -434
  71. package/src/v2/components/Customer/CustomerForm/index.ts +0 -1
  72. package/src/v2/components/Customer/CustomersList.tsx +0 -193
  73. package/src/v2/components/Customer/index.ts +0 -5
@@ -1,434 +0,0 @@
1
- import React, { useState, useEffect } from 'react'
2
- import { showAlert } from '@licklist/plugins/dist/context/app/AlertContext'
3
- import { NewInput } from '../../NewInput'
4
- import { Select } from '../../Select'
5
- import { Checkbox } from '../../Checkbox'
6
- import { Button } from '../../Button'
7
- import {useTranslation} from "react-i18next";
8
-
9
- export interface CustomerData {
10
- firstName: string
11
- lastName: string
12
- email: string
13
- dobDay: string
14
- dobMonth: string
15
- dobYear: string
16
- phone: string
17
- optIn: boolean
18
- postcode: string
19
- gender: string
20
- }
21
-
22
- export interface CustomerFormProps {
23
- onSave: (data: CustomerData) => void
24
- initialData?: Partial<CustomerData>
25
- isLoading?: boolean
26
- submitButtonLabel?: string
27
- isEditing?: boolean
28
- }
29
-
30
- export const CustomerForm: React.FC<CustomerFormProps> = ({
31
- onSave,
32
- initialData,
33
- isLoading,
34
- submitButtonLabel = 'Save Changes',
35
- isEditing = false,
36
- }) => {
37
- const STORAGE_KEY = 'customer_form_draft'
38
-
39
- const [formData, setFormData] = useState<CustomerData>(() => {
40
- // Try to restore from sessionStorage first
41
- try {
42
- const savedData = sessionStorage.getItem(STORAGE_KEY)
43
- if (savedData) {
44
- const parsed = JSON.parse(savedData)
45
- // If we have initialData (editing), prefer that over saved draft
46
- if (initialData?.email) {
47
- sessionStorage.removeItem(STORAGE_KEY) // Clear draft when editing
48
- return {
49
- firstName: '',
50
- lastName: '',
51
- email: '',
52
- dobDay: '',
53
- dobMonth: '',
54
- dobYear: '',
55
- phone: '',
56
- optIn: false,
57
- postcode: '',
58
- gender: '',
59
- ...initialData,
60
- }
61
- }
62
- return parsed
63
- }
64
- } catch (error) {
65
- console.error('Failed to restore form data:', error)
66
- }
67
-
68
- return {
69
- firstName: '',
70
- lastName: '',
71
- email: '',
72
- dobDay: '',
73
- dobMonth: '',
74
- dobYear: '',
75
- phone: '',
76
- optIn: false,
77
- postcode: '',
78
- gender: '',
79
- ...initialData,
80
- }
81
- })
82
-
83
- const [errors, setErrors] = useState<Partial<Record<keyof CustomerData, string>>>({})
84
- const { t } = useTranslation(['App'])
85
-
86
- // Save to sessionStorage whenever form data changes (but not when editing)
87
- useEffect(() => {
88
- if (!initialData?.email) { // Only save drafts for new customers
89
- try {
90
- sessionStorage.setItem(STORAGE_KEY, JSON.stringify(formData))
91
- } catch (error) {
92
- console.error('Failed to save form data:', error)
93
- }
94
- }
95
- }, [formData, initialData])
96
-
97
- useEffect(() => {
98
- if (initialData) {
99
- setFormData((prev) => ({ ...prev, ...initialData }))
100
- }
101
- }, [initialData])
102
-
103
- // Clear sessionStorage when component unmounts successfully (form submitted)
104
- useEffect(() => {
105
- return () => {
106
- // Only clear if we're not in the middle of validation error
107
- if (Object.keys(errors).length === 0) {
108
- sessionStorage.removeItem(STORAGE_KEY)
109
- }
110
- }
111
- }, [errors])
112
-
113
- const handleChange = (
114
- e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>,
115
- ) => {
116
- const { name, value, type } = e.target
117
- const checked = (e.target as HTMLInputElement).checked
118
-
119
- setFormData((prev) => ({
120
- ...prev,
121
- [name]: type === 'checkbox' ? checked : value,
122
- }))
123
-
124
- // Clear error when user types
125
- if (errors[name as keyof CustomerData]) {
126
- setErrors((prev) => {
127
- const newErrors = { ...prev }
128
- delete newErrors[name as keyof CustomerData]
129
- return newErrors
130
- })
131
- }
132
- }
133
-
134
- const handleSelectChange = (e: React.FormEvent<HTMLSelectElement>) => {
135
- const { name, value } = e.currentTarget
136
- setFormData((prev) => ({
137
- ...prev,
138
- [name]: value,
139
- }))
140
-
141
- // Clear error when user selects
142
- if (errors[name as keyof CustomerData]) {
143
- setErrors((prev) => {
144
- const newErrors = { ...prev }
145
- delete newErrors[name as keyof CustomerData]
146
- return newErrors
147
- })
148
- }
149
- }
150
-
151
- const validate = () => {
152
- const fieldErrors: Partial<Record<keyof CustomerData, string>> = {}
153
- const messages: string[] = []
154
-
155
- if (!formData.firstName.trim()) {
156
- fieldErrors.firstName = ' '
157
- //messages.push('First Name is required')
158
- messages.push(t('Validation:fieldRequired', { attribute: t('App:firstName') }))
159
- }
160
- if (!formData.lastName.trim()) {
161
- fieldErrors.lastName = ' '
162
- messages.push(t('Validation:fieldRequired', { attribute: t('App:lastName') }))
163
- }
164
-
165
- // Skip email validation when editing (field is readonly)
166
- if (!isEditing) {
167
- if (!formData.email.trim()) {
168
- fieldErrors.email = ' '
169
- messages.push(t('Validation:fieldRequired', { attribute: t('App:emailAddress') }))
170
- } else {
171
- // Basic email format check
172
- const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
173
- if (!emailRegex.test(formData.email)) {
174
- fieldErrors.email = ' '
175
- messages.push(t('App:emailAddressInvalid'))
176
- } else {
177
- // Additional validation for domain quality
178
- const parts = formData.email.split('@')
179
- if (parts.length === 2) {
180
- const domain = parts[1]
181
-
182
- // Check for valid TLD (at least 2 characters after last dot - allows .it, .co, etc.)
183
- const domainParts = domain.split('.')
184
- const tld = domainParts[domainParts.length - 1]
185
-
186
- // TLD must be at least 2 characters (allows .it, .co) but rejects single char like .c
187
- if (tld.length < 2 || !/^[a-zA-Z]+$/.test(tld)) {
188
- fieldErrors.email = ' '
189
- messages.push(t('App:emailAddressInvalid'))
190
- }
191
-
192
- // Check for consecutive dots or dots at wrong positions
193
- if (domain.includes('..') || domain.startsWith('.') || domain.endsWith('.')) {
194
- fieldErrors.email = ' '
195
- messages.push(t('App:emailAddressInvalid'))
196
- }
197
- }
198
- }
199
- }
200
- }
201
-
202
- // Optional Date of Birth validation
203
- const day = formData.dobDay?.trim()
204
- const month = formData.dobMonth?.trim()
205
- const year = formData.dobYear?.trim()
206
- const anyDobPart = !!(day || month || year)
207
-
208
- if (anyDobPart) {
209
- t('App:dateOfBirth', 'Date of Birth');
210
- const dayLabel = t('App:day', 'Day')
211
- const monthLabel = t('App:month', 'Month')
212
- const yearLabel = t('App:year', 'Year')
213
-
214
- // Require all three parts if any is provided
215
- if (!day) {
216
- fieldErrors.dobDay = ' '
217
- }
218
- if (!month) {
219
- fieldErrors.dobMonth = ' '
220
- }
221
- if (!year) {
222
- fieldErrors.dobYear = ' '
223
- }
224
- if (!day || !month || !year) {
225
- messages.push(t('Validation:fieldValidDate'))
226
- } else {
227
- // Numeric only checks
228
- const numOnly = /^\d+$/
229
- if (!numOnly.test(day)) {
230
- fieldErrors.dobDay = ' '
231
- messages.push(t('Validation:fieldOnlyNumbers', { attribute: dayLabel }))
232
- }
233
- if (!numOnly.test(month)) {
234
- fieldErrors.dobMonth = ' '
235
- messages.push(t('Validation:fieldOnlyNumbers', { attribute: monthLabel }))
236
- }
237
- if (!/^\d{4}$/.test(year)) {
238
- fieldErrors.dobYear = ' '
239
- // Prefer explicit invalid year formatting over generic numbers message to enforce 4 digits
240
- messages.push(t('Validation:fieldInvalid', { attribute: yearLabel }))
241
- } else if (!numOnly.test(year)) {
242
- fieldErrors.dobYear = ' '
243
- messages.push(t('Validation:fieldOnlyNumbers', { attribute: yearLabel }))
244
- }
245
-
246
- // Range checks if numeric
247
- const d = parseInt(day, 10)
248
- const m = parseInt(month, 10)
249
- const y = parseInt(year, 10)
250
-
251
- if (numOnly.test(day) && (d < 1 || d > 31)) {
252
- fieldErrors.dobDay = ' '
253
- messages.push(t('Validation:fieldValidDay', { attribute: dayLabel }))
254
- }
255
- if (numOnly.test(month) && (m < 1 || m > 12)) {
256
- fieldErrors.dobMonth = ' '
257
- messages.push(t('Validation:fieldValidMonth', { attribute: monthLabel }))
258
- }
259
-
260
- // Validate actual calendar date and future constraint
261
- if (numOnly.test(day) && numOnly.test(month) && /^\d{4}$/.test(year)) {
262
- const date = new Date(y, m - 1, d)
263
- const isValidDate =
264
- date.getFullYear() === y && date.getMonth() === m - 1 && date.getDate() === d
265
- if (!isValidDate) {
266
- fieldErrors.dobDay = ' '
267
- fieldErrors.dobMonth = ' '
268
- fieldErrors.dobYear = ' '
269
- messages.push(t('Validation:fieldValidDate'))
270
- } else {
271
- const today = new Date()
272
- const todayOnly = new Date(today.getFullYear(), today.getMonth(), today.getDate())
273
- if (date > todayOnly) {
274
- fieldErrors.dobDay = ' '
275
- fieldErrors.dobMonth = ' '
276
- fieldErrors.dobYear = ' '
277
- messages.push(t('Validation:birthdayInFuture'))
278
- }
279
- }
280
- }
281
- }
282
- }
283
-
284
- // Phone validation (Optional field, but must match format if provided)
285
- if (formData.phone?.trim()) {
286
- const phoneRegex = /^[+\-()\d\s]*$/
287
- if (!phoneRegex.test(formData.phone)) {
288
- fieldErrors.phone = ' '
289
- messages.push(t('Validation:fieldValidPhone', { attribute: t('App:phone') }))
290
- } else {
291
- // Enforce max of 15 numeric digits in total (ignoring spaces and symbols)
292
- const digitsCount = formData.phone.replace(/\D/g, '').length
293
- if (digitsCount > 15) {
294
- fieldErrors.phone = ' '
295
- messages.push(t('Validation:fieldMaxLength', { attribute: t('App:phone'), max: 15 }))
296
- }
297
- }
298
- }
299
-
300
- setErrors(fieldErrors)
301
- return { isValid: messages.length === 0, messages }
302
- }
303
-
304
- const handleSubmit = (e: React.FormEvent) => {
305
- e.preventDefault()
306
- const { isValid, messages } = validate()
307
- if (isValid) {
308
- // Clear draft from storage on successful validation
309
- try {
310
- sessionStorage.removeItem(STORAGE_KEY)
311
- } catch (error) {
312
- console.error('Failed to clear form data:', error)
313
- }
314
- onSave(formData)
315
- } else {
316
- showAlert({
317
- type: 'error',
318
- title: 'Form Error',
319
- message: messages,
320
- })
321
- }
322
- }
323
-
324
- return (
325
- <form onSubmit={handleSubmit} noValidate className="tw-flex tw-flex-col tw-gap-6">
326
- <NewInput
327
- label={t('App:firstName', "First")}
328
- name="firstName"
329
- value={formData.firstName}
330
- onChange={handleChange}
331
- error={errors.firstName}
332
- required
333
- />
334
-
335
- <NewInput
336
- label={t('App:lastName', "Last Name")}
337
- name="lastName"
338
- value={formData.lastName}
339
- onChange={handleChange}
340
- error={errors.lastName}
341
- required
342
- />
343
-
344
- <NewInput
345
- label={t('App:emailAddress', "Email Address")}
346
- name="email"
347
- type="text"
348
- value={formData.email}
349
- onChange={handleChange}
350
- error={errors.email}
351
- required
352
- readOnly={isEditing}
353
- disabled={isEditing}
354
- helperText={isEditing ? t('App:emailCannotBeEdited') : undefined}
355
- />
356
-
357
- <div className="tw-flex tw-flex-col tw-gap-2">
358
- <div className="tw-flex tw-items-center tw-gap-1">
359
- <label className="tw-text-[15px] tw-font-semibold tw-text-[#121E52] tw-leading-5">Date of Birth (Optional)</label>
360
- </div>
361
- <div className="tw-grid tw-grid-cols-3 tw-gap-4">
362
- <NewInput
363
- label={t("App:day", "Day")}
364
- name="dobDay"
365
- value={formData.dobDay}
366
- onChange={handleChange}
367
- error={errors.dobDay}
368
- className="dob-input"
369
- />
370
- <NewInput
371
- label={t("App:month", "Month")}
372
- name="dobMonth"
373
- value={formData.dobMonth}
374
- onChange={handleChange}
375
- error={errors.dobMonth}
376
- className="dob-input"
377
- />
378
- <NewInput
379
- label={t("App:year", "Year")}
380
- name="dobYear"
381
- value={formData.dobYear}
382
- onChange={handleChange}
383
- error={errors.dobYear}
384
- className="dob-input"
385
- />
386
- </div>
387
- </div>
388
-
389
- <NewInput
390
- label={t('App:phone', "Phone Number")}
391
- name="phone"
392
- value={formData.phone}
393
- onChange={handleChange}
394
- error={errors.phone}
395
- optional
396
- />
397
-
398
- <Checkbox
399
- label="Opt-in to marketing communications"
400
- name="optIn"
401
- checked={formData.optIn}
402
- onChange={handleChange}
403
- />
404
-
405
- <NewInput
406
- label={t('App:postcode', "Postcode")}
407
- name="postcode"
408
- value={formData.postcode}
409
- onChange={handleChange}
410
- optional
411
- />
412
-
413
- <Select
414
- label={t('App:gender', "Gender")}
415
- name="gender"
416
- value={formData.gender}
417
- onChange={handleSelectChange}
418
- optional
419
- >
420
- <option value="">{t('App:selectGender', "Select gender")}</option>
421
- <option value="female">{t('App:female', "Female")}</option>
422
- <option value="male">{t('App:male', "Male")}</option>
423
- <option value="prefer_not_to_say">{t('App:preferNotToSay', "Prefer not to say")}</option>
424
- </Select>
425
-
426
-
427
- <div className="tw-pt-4">
428
- <Button type="submit" disabled={isLoading} className="tw-px-8">
429
- {isLoading ? 'Saving...' : submitButtonLabel}
430
- </Button>
431
- </div>
432
- </form>
433
- )
434
- }
@@ -1 +0,0 @@
1
- export * from './CustomerForm'
@@ -1,193 +0,0 @@
1
- import React from 'react'
2
- import { QuickFilter, QuickFilterOption } from '../QuickFilter'
3
- import { NewTable, NewTableColumn } from '../NewTable'
4
- import { NewInput } from '../NewInput'
5
- import { Pagination } from '../Pagination'
6
- import { SearchIcon, RefreshIcon, SendIcon, ExternalLinkIcon, ExportIcon, ClearIcon } from '../../icons'
7
- import './CustomersList.scss'
8
-
9
- export interface CustomersListProps {
10
- t: (key: string, options?: any) => string
11
- customers: any[]
12
- isLoading: boolean
13
- kioskLink?: string
14
- waiversEnabled?: boolean
15
- search: string
16
- onSearchChange: (value: string) => void
17
- onSearchIconClick?: () => void
18
- onClearSearch?: () => void
19
- quickFilters: string[]
20
- onQuickFiltersChange: (values: string[]) => void
21
- onRefresh: () => void
22
- onExport: () => void
23
- onSendWaiverRequest: () => void
24
- onAddCustomer?: () => void
25
- onTabChange?: (tabId: string) => void
26
- activeTab?: string
27
- tabs?: { id: string; label: string; path: string }[]
28
- columns: NewTableColumn<any>[]
29
- pagination?: {
30
- currentPage: number
31
- totalPages: number
32
- onPageChange: (page: number) => void
33
- totalItems?: number
34
- itemsPerPage?: number
35
- }
36
- }
37
-
38
-
39
- export const CustomersList: React.FC<CustomersListProps> = ({
40
- t,
41
- customers,
42
- isLoading,
43
- kioskLink,
44
- search,
45
- onSearchChange,
46
- quickFilters,
47
- onSearchIconClick,
48
- onClearSearch,
49
- onQuickFiltersChange,
50
- onRefresh,
51
- onExport,
52
- onSendWaiverRequest,
53
- onTabChange,
54
- activeTab,
55
- tabs,
56
- columns,
57
- pagination,
58
- waiversEnabled,
59
- onAddCustomer,
60
- }) => {
61
- const quickFilterOptions: QuickFilterOption[] = waiversEnabled ? [
62
- { label: t('App:todayBookings'), value: 'today' },
63
- { label: t('App:signed', 'Signed'), value: 'signed' },
64
- { label: t('App:unsigned', 'Unsigned'), value: 'unsigned' },
65
- ] : [
66
- { label: t('App:todayBookings', 'Bookings for: Today'), value: 'today' },
67
- ]
68
-
69
- // Handle mutually exclusive signed/unsigned filters
70
- const handleQuickFiltersChange = (newFilters: string[]) => {
71
- const hasSigned = newFilters.includes('signed')
72
- const hasUnsigned = newFilters.includes('unsigned')
73
- const hadSigned = quickFilters.includes('signed')
74
- const hadUnsigned = quickFilters.includes('unsigned')
75
-
76
- // If both signed and unsigned are being selected, keep only the newly selected one
77
- if (hasSigned && hasUnsigned) {
78
- if (!hadSigned && hasUnsigned) {
79
- // signed was just added, remove unsigned
80
- onQuickFiltersChange(newFilters.filter(f => f !== 'unsigned'))
81
- } else if (hasSigned && !hadUnsigned) {
82
- // unsigned was just added, remove signed
83
- onQuickFiltersChange(newFilters.filter(f => f !== 'signed'))
84
- }
85
- } else {
86
- onQuickFiltersChange(newFilters)
87
- }
88
- }
89
-
90
- return (
91
- <div className="waivers-page">
92
- <header className="waivers-page__header">
93
- {tabs && tabs.length > 0 ? (
94
- <div className="waivers-page__tabs-container">
95
- <nav className="waivers-page__tabs">
96
- {tabs.map((tab) => (
97
- <button
98
- key={tab.id}
99
- className={`waivers-page__tab ${tab.id === activeTab ? 'waivers-page__tab--active' : ''}`}
100
- onClick={() => onTabChange?.(tab.id)}
101
- >
102
- {tab.label}
103
- {tab.id === activeTab && <div className="waivers-page__tab-indicator" />}
104
- </button>
105
- ))}
106
- </nav>
107
- {kioskLink && (
108
- <a href={kioskLink} target="_blank" rel="noopener noreferrer" className="kiosk-link">
109
- <ExternalLinkIcon />
110
- {t('App:launchKiosk', 'Launch Kiosk')}
111
- </a>
112
- )}
113
- </div>
114
- ) : (
115
- <div className="waivers-page__simple-header">
116
-
117
- {onAddCustomer && (
118
- <button className="waivers-page__add-customer-btn" onClick={onAddCustomer}>
119
- + {t('App:addCustomer', 'Add Customer')}
120
- </button>
121
- )}
122
- </div>
123
- )}
124
- </header>
125
-
126
- <main className="waivers-page__content">
127
- <section className="filter-section">
128
- <div className="search-row">
129
- <div className="search-input-wrapper">
130
- <NewInput
131
- label={t('App:search', 'Search')}
132
- value={search}
133
- onChange={(e) => onSearchChange(e.target.value)}
134
- onIconClick={search ? onClearSearch : onSearchIconClick}
135
- icon={search ? <ClearIcon /> : <SearchIcon />}
136
- iconPosition={search ? 'right' : 'left'}
137
- />
138
- <p className="search-helper-text">{t('App:searchPlaceholder')}</p>
139
- </div>
140
- </div>
141
-
142
- <div className="filters-actions-row">
143
- <div className="quick-filters-wrapper">
144
- <QuickFilter
145
- label={t('App:quickFilters', 'Quick Filters:')}
146
- options={quickFilterOptions}
147
- selectedValues={quickFilters}
148
- onChange={handleQuickFiltersChange}
149
- />
150
- </div>
151
- <div className="action-buttons">
152
- <button className="action-btn" onClick={onExport}>
153
- <ExportIcon />
154
- {t('App:exportData', 'Export')}
155
- </button>
156
- {waiversEnabled && (
157
- <button className="action-btn" onClick={onSendWaiverRequest}>
158
- <SendIcon />
159
- {t('App:requestWaiver', 'Request Waiver')}
160
- </button>
161
- )}
162
- <button className="action-btn" onClick={onRefresh}>
163
- <RefreshIcon />
164
- {t('App:refresh', 'Refresh')}
165
- </button>
166
- </div>
167
- </div>
168
- </section>
169
-
170
- <section className="table-section">
171
- <NewTable
172
- columns={columns}
173
- data={customers}
174
- noDataText={!isLoading && customers.length === 0 ? t('App:noResultsFound') : undefined}
175
- />
176
- {pagination && customers.length > 0 && (
177
- <div className="waivers-page__pagination">
178
- <Pagination
179
- currentPage={pagination.currentPage}
180
- totalPages={pagination.totalPages}
181
- totalItems={pagination.totalItems ?? 0}
182
- itemsPerPage={pagination.itemsPerPage ?? 24}
183
- onPageChange={pagination.onPageChange}
184
- t={t}
185
- entityName={t('App:customersLowerCase', 'customers')}
186
- />
187
- </div>
188
- )}
189
- </section>
190
- </main>
191
- </div>
192
- )
193
- }
@@ -1,5 +0,0 @@
1
- export * from './CustomersList'
2
- export * from './CustomerCreate'
3
- export * from './CustomerDetail'
4
- export * from './CustomerEdit'
5
- export * from './CustomerForm'