@star-insure/sdk 1.0.1 → 1.1.2

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 (95) hide show
  1. package/package.json +16 -12
  2. package/src/components/common/Button.tsx +60 -0
  3. package/src/components/common/Card.tsx +14 -0
  4. package/src/components/common/Modal.tsx +54 -0
  5. package/src/components/common/Pagination.tsx +77 -0
  6. package/src/components/common/ToastItem.tsx +54 -0
  7. package/src/components/common/Toasts.tsx +16 -0
  8. package/src/components/common/index.ts +15 -0
  9. package/src/components/forms/DateOfBirthField.tsx +84 -0
  10. package/src/components/forms/Errors.tsx +32 -0
  11. package/src/components/forms/FormTester.tsx +170 -0
  12. package/src/components/forms/MoneyField.tsx +45 -0
  13. package/src/components/forms/RegistrationSearchField.tsx +187 -0
  14. package/src/components/forms/index.ts +13 -0
  15. package/src/components/index.ts +3 -0
  16. package/src/components/tables/Table.tsx +19 -0
  17. package/src/components/tables/TableActions.tsx +14 -0
  18. package/src/components/tables/TableBody.tsx +14 -0
  19. package/src/components/tables/TableCell.tsx +14 -0
  20. package/src/components/tables/TableHead.tsx +14 -0
  21. package/src/components/tables/TableHeader.tsx +49 -0
  22. package/src/components/tables/TableRow.tsx +17 -0
  23. package/src/components/tables/index.ts +17 -0
  24. package/src/index.ts +1 -0
  25. package/src/lib/addressFinder.tsx +100 -0
  26. package/src/lib/auth.tsx +8 -0
  27. package/src/lib/calculateAge.ts +19 -0
  28. package/src/lib/clickOutside.tsx +24 -0
  29. package/src/lib/index.ts +9 -0
  30. package/src/lib/inertiaOptions.tsx +27 -0
  31. package/src/lib/localStorage.tsx +41 -0
  32. package/src/lib/quoteRequestForm.tsx +144 -0
  33. package/src/lib/quoteRequestOptions.tsx +21 -0
  34. package/src/lib/toast.tsx +55 -0
  35. package/dist/index.d.ts +0 -2
  36. package/dist/index.js +0 -8
  37. package/dist/lib/dates.d.ts +0 -8
  38. package/dist/lib/index.d.ts +0 -3
  39. package/dist/lib/money.d.ts +0 -32
  40. package/dist/lib/vehicles.d.ts +0 -2
  41. package/dist/sdk.cjs.development.js +0 -161
  42. package/dist/sdk.cjs.development.js.map +0 -1
  43. package/dist/sdk.cjs.production.min.js +0 -2
  44. package/dist/sdk.cjs.production.min.js.map +0 -1
  45. package/dist/sdk.esm.js +0 -148
  46. package/dist/sdk.esm.js.map +0 -1
  47. package/dist/types/api/auth.d.ts +0 -9
  48. package/dist/types/api/enums.d.ts +0 -4
  49. package/dist/types/api/index.d.ts +0 -31
  50. package/dist/types/api/responses/claimRequestActions.d.ts +0 -12
  51. package/dist/types/api/responses/index.d.ts +0 -1
  52. package/dist/types/index.d.ts +0 -3
  53. package/dist/types/misc/index.d.ts +0 -8
  54. package/dist/types/misc/inertia.d.ts +0 -8
  55. package/dist/types/models/auth/Group.d.ts +0 -10
  56. package/dist/types/models/auth/Permission.d.ts +0 -4
  57. package/dist/types/models/auth/Role.d.ts +0 -6
  58. package/dist/types/models/auth/User.d.ts +0 -12
  59. package/dist/types/models/auth/index.d.ts +0 -4
  60. package/dist/types/models/claims/ClaimRequest.d.ts +0 -287
  61. package/dist/types/models/claims/index.d.ts +0 -1
  62. package/dist/types/models/index.d.ts +0 -6
  63. package/dist/types/models/oracle/Risk.d.ts +0 -8
  64. package/dist/types/models/oracle/TableData.d.ts +0 -9
  65. package/dist/types/models/oracle/index.d.ts +0 -2
  66. package/dist/types/models/payments/Gateways.d.ts +0 -1
  67. package/dist/types/models/payments/Payment.d.ts +0 -35
  68. package/dist/types/models/payments/index.d.ts +0 -2
  69. package/dist/types/models/quotes/Club.d.ts +0 -4
  70. package/dist/types/models/quotes/EmailContentOption.d.ts +0 -7
  71. package/dist/types/models/quotes/InformationRequest.d.ts +0 -34
  72. package/dist/types/models/quotes/Lead.d.ts +0 -17
  73. package/dist/types/models/quotes/MotorwebVehicle.d.ts +0 -120
  74. package/dist/types/models/quotes/PolicyBenefit.d.ts +0 -16
  75. package/dist/types/models/quotes/PolicyEnhancement.d.ts +0 -11
  76. package/dist/types/models/quotes/PostalAddress.d.ts +0 -7
  77. package/dist/types/models/quotes/PromoCode.d.ts +0 -18
  78. package/dist/types/models/quotes/QuoteRequest.d.ts +0 -88
  79. package/dist/types/models/quotes/QuoteRequestDeclaration.d.ts +0 -22
  80. package/dist/types/models/quotes/QuoteRequestForm.d.ts +0 -112
  81. package/dist/types/models/quotes/QuoteRequestIncident.d.ts +0 -5
  82. package/dist/types/models/quotes/QuoteRequestLog.d.ts +0 -10
  83. package/dist/types/models/quotes/QuoteRequestOptions.d.ts +0 -59
  84. package/dist/types/models/quotes/QuoteRequestPurchaseOption.d.ts +0 -35
  85. package/dist/types/models/quotes/QuoteRequestReferrer.d.ts +0 -7
  86. package/dist/types/models/quotes/QuoteRequestReferrerCategory.d.ts +0 -4
  87. package/dist/types/models/quotes/QuoteRequestVehicle.d.ts +0 -20
  88. package/dist/types/models/quotes/QuoteRequestVehicleDriver.d.ts +0 -10
  89. package/dist/types/models/quotes/RedbookVehicle.d.ts +0 -55
  90. package/dist/types/models/quotes/StreetAddress.d.ts +0 -8
  91. package/dist/types/models/quotes/index.d.ts +0 -22
  92. package/dist/types/models/sms-messages/SmsContentOption.d.ts +0 -7
  93. package/dist/types/models/sms-messages/SmsMessage.d.ts +0 -18
  94. package/dist/types/models/sms-messages/SmsMessageReply.d.ts +0 -10
  95. package/dist/types/models/sms-messages/index.d.ts +0 -3
@@ -0,0 +1,45 @@
1
+ import React from 'react';
2
+ import { formatMoney, formatNumber } from "../../lib";
3
+
4
+ interface Props {
5
+ value?: number;
6
+ onChange: (value: number) => void;
7
+ id?: string;
8
+ name?: string;
9
+ className?: string;
10
+ required?: boolean;
11
+ }
12
+
13
+ export default function MoneyField({ value = 0, onChange, name = 'value', id = 'value', className = '', required = false }: Props) {
14
+ function handleChange(e: React.SyntheticEvent<HTMLInputElement>) {
15
+ let newValue: number = 0;
16
+
17
+ if (e.currentTarget.value) {
18
+ newValue = formatNumber(e.currentTarget.value);
19
+
20
+ if (isNaN(newValue)) {
21
+ newValue = 0;
22
+ }
23
+ }
24
+
25
+ onChange(newValue);
26
+ }
27
+
28
+ const displayValue = formatMoney(value, 0);
29
+
30
+ return (
31
+ <div className={`${className} flex items-center gap-2 pl-4 rounded-md border border-gray-300 transition-all focus-within:border-teal focus-within:outline-none focus-within:ring focus-within:ring-teal focus-within:ring-opacity-50`}>
32
+ <span className="font-bold">$</span>
33
+ <input
34
+ type="text"
35
+ name={name}
36
+ id={id}
37
+ value={displayValue}
38
+ onChange={handleChange}
39
+ className="w-full !focus:outline-none !focus:ring-0 !focus:ring-transparent !focus:ring-opacity-0 !ring-0 !border-0 !transition-none"
40
+ required={required}
41
+ />
42
+ </div>
43
+ );
44
+
45
+ }
@@ -0,0 +1,187 @@
1
+ import React from 'react';
2
+ import { sanitiseVehicleType } from '../../lib';
3
+ import { ApiResponse, FormStatus, MotorwebVehicleResponse, VehicleType } from '../../types';
4
+ import { useToast } from '../../lib/toast';
5
+
6
+ export interface VehicleData extends MotorwebVehicleResponse {
7
+ make: string;
8
+ model: string;
9
+ year: string;
10
+ vehicle_type: VehicleType;
11
+ is_heavy: boolean;
12
+ }
13
+
14
+ export interface RegistrationSearchOnChange {
15
+ registration: string;
16
+ }
17
+
18
+ interface Props {
19
+ onChange?: (values: RegistrationSearchOnChange) => void;
20
+ onVehicleDataFound: (vehicleData: VehicleData) => void;
21
+ apiUrl?: string;
22
+ showOdometerReadingField?: boolean;
23
+ showConditionField?: boolean;
24
+ initialRegistrationValue?: string;
25
+ }
26
+
27
+ export const conditionOptions = ['POOR', 'FAIR', 'AVERAGE', 'GOOD', 'VERY_GOOD', 'AS_NEW', 'NEW'];
28
+
29
+ export default function RegistrationSearchField({
30
+ initialRegistrationValue = '',
31
+ onVehicleDataFound,
32
+ apiUrl = "/api/rego-search",
33
+ showOdometerReadingField = false,
34
+ showConditionField = false,
35
+ onChange,
36
+ }: Props) {
37
+
38
+ const [rego, setRego] = React.useState<string>(initialRegistrationValue);
39
+ const [odo, setOdo] = React.useState<string>('');
40
+ const [condition, setCondition] = React.useState<string>('GOOD');
41
+
42
+ const [status, setStatus] = React.useState<FormStatus>('idle');
43
+ const { addToast } = useToast();
44
+
45
+ function handleChange(e: React.SyntheticEvent<HTMLInputElement|HTMLSelectElement>) {
46
+ if (e.currentTarget.name === 'registration_search') {
47
+ setRego(e.currentTarget.value?.toUpperCase() || '');
48
+ }
49
+
50
+ if (e.currentTarget.name === 'odo_search') {
51
+ setOdo(e.currentTarget.value);
52
+ }
53
+
54
+ if (e.currentTarget.name === 'condition_search') {
55
+ setCondition(e.currentTarget.value);
56
+ }
57
+
58
+ if (onChange) {
59
+ onChange({ registration: e.currentTarget.value.toUpperCase() });
60
+ }
61
+ }
62
+
63
+ function handleKeydown(e: React.KeyboardEvent) {
64
+ if (e.key === 'Enter') {
65
+ handleSearch();
66
+ }
67
+ }
68
+
69
+ async function handleSearch() {
70
+ setStatus('processing');
71
+
72
+ const payload = {
73
+ registration: rego,
74
+ odometerReading: odo,
75
+ condition,
76
+ };
77
+
78
+ // Turn the payload in to a query string
79
+ // @ts-ignore
80
+ const query = new URLSearchParams(payload).toString();
81
+
82
+ const res = await fetch(`${apiUrl}?${query}`, {
83
+ headers: { 'Content-Type': 'application/json' }
84
+ });
85
+
86
+ if (!res.ok) {
87
+ addToast({ message: `There was an error with the provided registration.`, status: 'error', timeout: 3000 });
88
+ setStatus('error');
89
+ return;
90
+ }
91
+
92
+ const { data, ok }: ApiResponse<MotorwebVehicleResponse> = await res.json();
93
+
94
+ if (!ok || !data) {
95
+ addToast({ message: `Failed to find vehicle, please complete manually.`, status: 'error', timeout: 3000 });
96
+ setStatus('error');
97
+ return;
98
+ }
99
+
100
+ const vehicleData = data.data.vehicle;
101
+
102
+ const make = vehicleData.make;
103
+ const model = vehicleData.model;
104
+ const year = vehicleData['year-of-manufacture'];
105
+ const weight = vehicleData['gross-vehicle-mass'] ? parseInt(vehicleData['gross-vehicle-mass'].replaceAll(',', '')) : 0;
106
+ const vehicleType = vehicleData['vehicle-type'];
107
+
108
+ if (!make || !model || !year || !vehicleType) {
109
+ addToast({ message: `We couldn't find all of your data, please complete manually.`, status: 'error', timeout: 3000 });
110
+ setStatus('error');
111
+ }
112
+
113
+ onVehicleDataFound({
114
+ ...data,
115
+ make: make || '',
116
+ model: model || '',
117
+ year: year || '',
118
+ vehicle_type: vehicleType ? sanitiseVehicleType(vehicleType) : 'car',
119
+ is_heavy: weight && weight > 3500 || false,
120
+ });
121
+
122
+ setStatus('success');
123
+ }
124
+
125
+ return (
126
+ <div className="w-full flex flex-col">
127
+ <div className="w-full flex flex-wrap gap-3 bg-gray-100 rounded-md p-3">
128
+ {showOdometerReadingField || showConditionField && (
129
+ <div className="w-full grid grid-cols-2 gap-3">
130
+ {showOdometerReadingField && (
131
+ <label>
132
+ Approx. km travelled
133
+ <input
134
+ onKeyDown={handleKeydown}
135
+ type="number"
136
+ name="odo_search"
137
+ id="odo_search"
138
+ value={odo}
139
+ onChange={handleChange}
140
+ className={`transition-all ${status === 'processing' ? 'pointer-events-none opacity-50' : ''}`}
141
+ disabled={status === 'processing'}
142
+ />
143
+ </label>
144
+ )}
145
+
146
+ {showConditionField && (
147
+ <label>
148
+ Vehicle condition
149
+ <select
150
+ name="condition_search"
151
+ id="condition_search"
152
+ value={condition}
153
+ onChange={handleChange}
154
+ className={`transition-all ${status === 'processing' ? 'pointer-events-none opacity-50' : ''}`}
155
+ disabled={status === 'processing'}
156
+ >
157
+ {conditionOptions.map(conditionOption => (
158
+ <option key={conditionOption}>{conditionOption}</option>
159
+ ))}
160
+ </select>
161
+ </label>
162
+ )}
163
+ </div>
164
+ )}
165
+
166
+ <input
167
+ onKeyDown={handleKeydown}
168
+ placeholder="Search for your rego"
169
+ type="text"
170
+ name="registration_search"
171
+ id="registration_search"
172
+ value={rego}
173
+ onChange={handleChange}
174
+ className={`flex-grow transition-all ${status === 'processing' ? 'pointer-events-none opacity-50' : ''}`}
175
+ disabled={status === 'processing'}
176
+ />
177
+
178
+ <button title="Search" type="button" onClick={handleSearch} disabled={status === 'processing'} className={`bg-teal px-4 py-3 rounded-md transition-all`}>
179
+ <span className="sr-only">Search</span>
180
+ <svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
181
+ <path strokeLinecap="round" strokeLinejoin="round" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
182
+ </svg>
183
+ </button>
184
+ </div>
185
+ </div>
186
+ )
187
+ }
@@ -0,0 +1,13 @@
1
+ import DateOfBirthField from './DateOfBirthField';
2
+ import MoneyField from './MoneyField';
3
+ import RegistrationSearchField from './RegistrationSearchField';
4
+ import FormTester from './FormTester';
5
+ import Errors from './Errors';
6
+
7
+ export {
8
+ DateOfBirthField,
9
+ MoneyField,
10
+ RegistrationSearchField,
11
+ FormTester,
12
+ Errors,
13
+ }
@@ -0,0 +1,3 @@
1
+ export * from './common';
2
+ export * from './tables';
3
+ export * from './forms';
@@ -0,0 +1,19 @@
1
+ import React from 'react';
2
+
3
+ interface Props {
4
+ children: React.ReactNode;
5
+ }
6
+
7
+ export default function Table({ children }: Props) {
8
+ return (
9
+ <div className="-my-2 -mx-4 overflow-x-auto sm:-mx-6 lg:-mx-8 text-sm">
10
+ <div className="inline-block min-w-full py-2 align-middle md:px-6 lg:px-8">
11
+ <div className="overflow-hidden shadow ring-1 ring-black ring-opacity-5 md:rounded-lg">
12
+ <table className="min-w-full divide-y divide-gray-300">
13
+ {children}
14
+ </table>
15
+ </div>
16
+ </div>
17
+ </div>
18
+ )
19
+ }
@@ -0,0 +1,14 @@
1
+ import React from 'react';
2
+
3
+ interface Props {
4
+ children: React.ReactNode;
5
+ className?: string;
6
+ }
7
+
8
+ export default function TableActions({ children, className = '' }: Props) {
9
+ return (
10
+ <div className={`${className} flex gap-4 justify-end items-center`}>
11
+ {children}
12
+ </div>
13
+ )
14
+ }
@@ -0,0 +1,14 @@
1
+ import React from 'react';
2
+
3
+ interface Props {
4
+ children: React.ReactNode;
5
+ className?: string;
6
+ }
7
+
8
+ export default function TableBody({ children, className = '' }: Props) {
9
+ return (
10
+ <tbody className={`${className ?? ''} divide-y divide-gray-200 bg-white`}>
11
+ {children}
12
+ </tbody>
13
+ )
14
+ }
@@ -0,0 +1,14 @@
1
+ import React from 'react';
2
+
3
+ interface Props {
4
+ children: React.ReactNode;
5
+ className?: string;
6
+ }
7
+
8
+ export default function TableCell({ children, className = '' }: Props) {
9
+ return (
10
+ <td className={`${className} px-3 py-3 text-sm text-gray-500`}>
11
+ {children}
12
+ </td>
13
+ )
14
+ }
@@ -0,0 +1,14 @@
1
+ import React from 'react';
2
+
3
+ interface Props {
4
+ children: React.ReactNode;
5
+ className?: string;
6
+ }
7
+
8
+ export default function TableHead({ children, className = '' }: Props) {
9
+ return (
10
+ <thead className={`${className} px-3 py-4 text-sm text-white bg-gray-600`}>
11
+ {children}
12
+ </thead>
13
+ )
14
+ }
@@ -0,0 +1,49 @@
1
+ import React from "react";
2
+ import { Link } from "@inertiajs/inertia-react";
3
+
4
+ interface Props {
5
+ children: React.ReactNode;
6
+ className?: string;
7
+ textAlign?: 'left' | 'right' | 'center';
8
+ sort?: string;
9
+ }
10
+
11
+ export default function TableHeader({ children, className = '', sort, textAlign = 'left' }: Props) {
12
+ const [sortLink, setSortLink] = React.useState<string>('');
13
+
14
+ React.useEffect(() => {
15
+ if (typeof window !== 'undefined' && sort) {
16
+ const query = new URLSearchParams(window.location.search);
17
+
18
+ let direction: 'asc' | 'desc' = 'asc';
19
+
20
+ if (query.get('sort')) {
21
+ // Choose the opposite direction for sorting
22
+ direction = query.get('sort')?.includes('asc') ? 'desc' : 'asc';
23
+ }
24
+
25
+ query.set('sort', `${sort} ${direction}`);
26
+
27
+ setSortLink(`?${query.toString()}`);
28
+ }
29
+ }, []);
30
+
31
+ const textAlignClass = textAlign === 'center' && 'text-center justify-center'
32
+ || textAlign === 'right' && 'text-right justify-end'
33
+ || 'text-left justify-between';
34
+
35
+ return (
36
+ <th className={`${className} py-3.5 px-3 text-sm font-semibold text-left`}>
37
+ <div className={`flex items-center gap-3 ${textAlignClass}`}>
38
+ {children}
39
+ {sort && (
40
+ <Link href={sortLink}>
41
+ <svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
42
+ <path strokeLinecap="round" strokeLinejoin="round" d="M8 9l4-4 4 4m0 6l-4 4-4-4" />
43
+ </svg>
44
+ </Link>
45
+ )}
46
+ </div>
47
+ </th>
48
+ )
49
+ }
@@ -0,0 +1,17 @@
1
+ import React from 'react';
2
+
3
+ interface Props {
4
+ children: React.ReactNode;
5
+ className?: string;
6
+ onClick?: () => void;
7
+ }
8
+
9
+ export default function TableRow({ children, className = '', onClick = () => {} }: Props) {
10
+ const bgClass = className.includes('bg') ? '' : 'bg-white even:bg-gray-50'
11
+
12
+ return (
13
+ <tr className={`${className} ${bgClass} hover:bg-gray-100`} onClick={onClick}>
14
+ {children}
15
+ </tr>
16
+ )
17
+ }
@@ -0,0 +1,17 @@
1
+ import Table from './Table';
2
+ import TableActions from './TableActions';
3
+ import TableBody from './TableBody';
4
+ import TableCell from './TableCell';
5
+ import TableHead from './TableHead';
6
+ import TableHeader from './TableHeader';
7
+ import TableRow from './TableRow';
8
+
9
+ export {
10
+ Table,
11
+ TableActions,
12
+ TableBody,
13
+ TableCell,
14
+ TableHead,
15
+ TableHeader,
16
+ TableRow,
17
+ }
package/src/index.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export * from './types';
2
2
  export * from './lib';
3
+ export * from './components';
@@ -0,0 +1,100 @@
1
+ const prodConfig = {
2
+ key: 'XVAKN4Q9PREJD8CUTFBW',
3
+ baseUrl: 'https://api.addressfinder.io/api/nz/address',
4
+ }
5
+
6
+ const config = prodConfig;
7
+
8
+ interface AutocompleteResponse {
9
+ completions: AddressCompletion[];
10
+ paid: boolean;
11
+ demo: boolean;
12
+ success: boolean;
13
+ }
14
+
15
+ export interface AddressCompletion {
16
+ a: string;
17
+ pxid: string;
18
+ v: number;
19
+ }
20
+
21
+ export interface AddressData {
22
+ pxid: string;
23
+ a: string;
24
+ aims_address_id: string;
25
+ sufi: number;
26
+ ta_id: string;
27
+ ta: string;
28
+ tasub_id: string;
29
+ tasub: string;
30
+ number: string;
31
+ x: string;
32
+ y: string;
33
+ postcode: string;
34
+ street: string;
35
+ street_name: string;
36
+ street_type: string;
37
+ city: string;
38
+ suburb: string;
39
+ region_id: string;
40
+ region: string;
41
+ postal_line_1: string;
42
+ postal_line_2: string;
43
+ aims_road_section_id: string;
44
+ rural: string;
45
+ address_line_1: string;
46
+ address_line_2?: string;
47
+ primary_parcel_id: string;
48
+ meshblock: string;
49
+ sa1_id: string;
50
+ sa2_id: string;
51
+ sa2: string;
52
+ cb_id: string;
53
+ cb: string;
54
+ ward_id: string;
55
+ ward: string;
56
+ con_id: string;
57
+ con: string;
58
+ maoricon_id: string;
59
+ maoricon: string;
60
+ iur_id: string;
61
+ iur: string;
62
+ ur_id: string;
63
+ ur: string;
64
+ landwater_id: string;
65
+ landwater: string;
66
+ success: boolean;
67
+ }
68
+
69
+ export async function autocomplete(search: string): Promise<AddressCompletion[]> {
70
+ const { key, baseUrl } = config;
71
+ const q = encodeURIComponent(search);
72
+
73
+ const res = await fetch(`${baseUrl}/autocomplete/?key=${key}&q=${q}&format=json&strict=2`);
74
+
75
+ if (!res.ok) {
76
+ return [];
77
+ }
78
+
79
+ const data: AutocompleteResponse = await res.json();
80
+
81
+ if (data.completions) {
82
+ return data.completions;
83
+ }
84
+
85
+ return [];
86
+ }
87
+
88
+ export async function getAddressData(completion: AddressCompletion): Promise<AddressData|null> {
89
+ const { key, baseUrl } = config;
90
+
91
+ const res = await fetch(`${baseUrl}/metadata/?key=${key}&pxid=${completion.pxid}&format=json`);
92
+
93
+ if (!res.ok) {
94
+ return null;
95
+ }
96
+
97
+ const data: AddressData = await res.json();
98
+
99
+ return data;
100
+ }
@@ -0,0 +1,8 @@
1
+ import { usePage } from '@inertiajs/inertia-react';
2
+ import type { AuthContext, PageProps } from '../types';
3
+
4
+ export function useAuth(): AuthContext {
5
+ const { props } = usePage<PageProps>() as PageProps;
6
+
7
+ return props.auth;
8
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Calculate someone's age from a date of birth.
3
+ * @param date YYYY-MM-DD
4
+ */
5
+ export function calculateAge(date: string): number {
6
+ const today = new Date();
7
+
8
+ const birthDate = new Date(date);
9
+
10
+ const age = today.getFullYear() - birthDate.getFullYear();
11
+
12
+ const m = today.getMonth() - birthDate.getMonth();
13
+
14
+ if (m < 0 || (m === 0 && today.getDate() < birthDate.getDate())) {
15
+ return age - 1;
16
+ }
17
+
18
+ return age;
19
+ }
@@ -0,0 +1,24 @@
1
+ import { useEffect } from 'react';
2
+
3
+ export function useClickOutside(ref: any, handler: (e: Event) => void) {
4
+ useEffect(
5
+ () => {
6
+ const listener = (event: Event) => {
7
+ // Do nothing if clicking ref's element or descendent elements
8
+ if (!ref.current || ref.current.contains(event.target)) {
9
+ return;
10
+ }
11
+ handler(event);
12
+ };
13
+
14
+ document.addEventListener('mousedown', listener);
15
+ document.addEventListener('touchstart', listener);
16
+
17
+ return () => {
18
+ document.removeEventListener('mousedown', listener);
19
+ document.removeEventListener('touchstart', listener);
20
+ };
21
+ },
22
+ [ref, handler]
23
+ );
24
+ }
package/src/lib/index.ts CHANGED
@@ -1,3 +1,12 @@
1
1
  export * from './dates';
2
2
  export * from './money';
3
3
  export * from './vehicles';
4
+ export * from './auth';
5
+ export * from './clickOutside';
6
+ export * from './inertiaOptions';
7
+ export * from './localStorage';
8
+ export * from './toast';
9
+ export * from './quoteRequestForm';
10
+ export * from './quoteRequestOptions';
11
+ export * from './addressFinder';
12
+ export * from './calculateAge';
@@ -0,0 +1,27 @@
1
+ import type { VisitOptions } from '@inertiajs/inertia/types';
2
+ import { useToast } from './toast';
3
+
4
+ interface Options {
5
+ successMessage?: string;
6
+ }
7
+
8
+ export function useInertiaOptions({ successMessage = '' }: Options): VisitOptions {
9
+ const { addToast } = useToast();
10
+
11
+ return {
12
+ onSuccess: () => {
13
+ addToast({
14
+ message: successMessage ?? 'Your request was successful.',
15
+ status: 'success',
16
+ });
17
+ },
18
+ onError: (errors) => {
19
+ Object.values(errors).forEach((err) => {
20
+ addToast({
21
+ message: err,
22
+ status: 'error',
23
+ });
24
+ });
25
+ }
26
+ }
27
+ }
@@ -0,0 +1,41 @@
1
+ import { useState } from "react";
2
+
3
+ export function useLocalStorage<T>(key: string, initialValue: T) {
4
+ // State to store our value
5
+ // Pass initial state function to useState so logic is only executed once
6
+ const [storedValue, setStoredValue] = useState<T>(() => {
7
+ if (typeof window === "undefined") {
8
+ return initialValue;
9
+ }
10
+ try {
11
+ // Get from local storage by key
12
+ const item = window.localStorage.getItem(key);
13
+ // Parse stored json or if none return initialValue
14
+ return item ? JSON.parse(item) : initialValue;
15
+ } catch (error) {
16
+ // If error also return initialValue
17
+ console.log(error);
18
+ return initialValue;
19
+ }
20
+ });
21
+ // Return a wrapped version of useState's setter function that ...
22
+ // ... persists the new value to localStorage.
23
+ const setValue = (value: T | ((val: T) => T)) => {
24
+ try {
25
+ // Allow value to be a function so we have same API as useState
26
+ const valueToStore =
27
+ value instanceof Function ? value(storedValue) : value;
28
+ // Save state
29
+ setStoredValue(valueToStore);
30
+ // Save to local storage
31
+ if (typeof window !== "undefined") {
32
+ window.localStorage.setItem(key, JSON.stringify(valueToStore));
33
+ }
34
+ } catch (error) {
35
+ // A more advanced implementation would handle the error case
36
+ console.log(error);
37
+ }
38
+ };
39
+
40
+ return [storedValue, setValue] as const;
41
+ }