@getmicdrop/svelte-components 5.18.2 → 5.20.0
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/base.css +18 -0
- package/dist/calendar/Calendar/MiniMonthCalendar.svelte +10 -8
- package/dist/calendar/Calendar/MiniMonthCalendar.svelte.d.ts.map +1 -1
- package/dist/components/Heading.svelte +8 -2
- package/dist/components/Heading.svelte.d.ts +1 -0
- package/dist/components/Heading.svelte.d.ts.map +1 -1
- package/dist/components/Text.svelte +13 -2
- package/dist/components/Text.svelte.d.ts +2 -1
- package/dist/components/Text.svelte.d.ts.map +1 -1
- package/dist/constants/formOptions.d.ts +2 -5
- package/dist/constants/formOptions.d.ts.map +1 -1
- package/dist/constants/formOptions.js +6 -6
- package/dist/constants/formOptions.spec.js +2 -7
- package/dist/index.d.ts +6 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +29 -2
- package/dist/patterns/forms/FormSection.svelte +2 -1
- package/dist/patterns/forms/FormSection.svelte.d.ts +2 -0
- package/dist/patterns/forms/FormSection.svelte.d.ts.map +1 -1
- package/dist/patterns/layout/SidebarTestWrapper.svelte.d.ts +1 -1
- package/dist/patterns/navigation/Header.svelte +3 -3
- package/dist/primitives/Input/Select.svelte +1 -1
- package/dist/primitives/Pagination/DotIndicator.svelte +66 -0
- package/dist/primitives/Pagination/DotIndicator.svelte.d.ts +18 -0
- package/dist/primitives/Pagination/DotIndicator.svelte.d.ts.map +1 -0
- package/dist/primitives/index.d.ts +1 -0
- package/dist/primitives/index.js +1 -0
- package/dist/recipes/inputs/phoneInput/CountrySelector.svelte +1 -1
- package/dist/recipes/modals/FeedbackModal.svelte +205 -0
- package/dist/recipes/modals/FeedbackModal.svelte.d.ts +24 -0
- package/dist/recipes/modals/FeedbackModal.svelte.d.ts.map +1 -0
- package/dist/recipes/modals/index.d.ts +1 -0
- package/dist/recipes/modals/index.js +1 -0
- package/dist/schemas/event.d.ts +4 -4
- package/dist/schemas/order.d.ts +2 -2
- package/dist/schemas/promo.d.ts +4 -4
- package/dist/services/event.service.d.ts +11 -0
- package/dist/services/event.service.d.ts.map +1 -0
- package/dist/services/event.service.js +64 -0
- package/dist/services/event.service.spec.d.ts +2 -0
- package/dist/services/event.service.spec.d.ts.map +1 -0
- package/dist/services/event.service.spec.js +168 -0
- package/dist/services/{ShowService.d.ts → show.service.d.ts} +1 -1
- package/dist/services/show.service.d.ts.map +1 -0
- package/dist/services/show.service.js +115 -0
- package/dist/services/show.service.spec.d.ts +2 -0
- package/dist/services/show.service.spec.d.ts.map +1 -0
- package/dist/services/show.service.spec.js +242 -0
- package/dist/tailwind/preset.cjs +5 -0
- package/dist/tailwind/preset.d.cts +2 -0
- package/dist/tailwind/preset.d.cts.map +1 -1
- package/dist/tokens/__tests__/spacing.test.js +2 -2
- package/dist/tokens/base-resets.css +124 -0
- package/dist/tokens/spacing.d.ts +2 -0
- package/dist/tokens/spacing.d.ts.map +1 -1
- package/dist/tokens/spacing.js +1 -0
- package/dist/tokens/tokens.css +1 -1
- package/dist/tokens/utilities.css +79 -2
- package/dist/utils/apiConfig.js +1 -1
- package/dist/utils/apiConfig.spec.js +34 -27
- package/dist/utils/assets.d.ts +3 -0
- package/dist/utils/assets.d.ts.map +1 -0
- package/dist/utils/assets.js +3 -0
- package/dist/utils/classNames.d.ts +10 -0
- package/dist/utils/classNames.d.ts.map +1 -0
- package/dist/utils/classNames.js +15 -0
- package/dist/utils/clickOutside.d.ts +4 -0
- package/dist/utils/clickOutside.d.ts.map +1 -0
- package/dist/utils/clickOutside.js +13 -0
- package/dist/utils/cookieHelpers.d.ts +40 -0
- package/dist/utils/cookieHelpers.d.ts.map +1 -0
- package/dist/utils/cookieHelpers.js +102 -0
- package/dist/utils/dateHelpers.d.ts +71 -0
- package/dist/utils/dateHelpers.d.ts.map +1 -0
- package/dist/utils/dateHelpers.js +253 -0
- package/dist/utils/eventFormatters.d.ts +9 -0
- package/dist/utils/eventFormatters.d.ts.map +1 -0
- package/dist/utils/eventFormatters.js +96 -0
- package/dist/utils/feedbackContext.d.ts +24 -0
- package/dist/utils/feedbackContext.d.ts.map +1 -0
- package/dist/utils/feedbackContext.js +19 -0
- package/dist/utils/fetchHelpers.d.ts +17 -0
- package/dist/utils/fetchHelpers.d.ts.map +1 -0
- package/dist/utils/fetchHelpers.js +45 -0
- package/dist/utils/focusTrap.d.ts +20 -0
- package/dist/utils/focusTrap.d.ts.map +1 -0
- package/dist/utils/focusTrap.js +130 -0
- package/dist/utils/formatters.d.ts +56 -0
- package/dist/utils/formatters.d.ts.map +1 -1
- package/dist/utils/formatters.js +121 -1
- package/dist/utils/formatters.spec.js +128 -1
- package/dist/utils/logger.d.ts +25 -1
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +59 -1
- package/dist/utils/logger.spec.js +99 -1
- package/dist/utils/permissions.d.ts +9 -0
- package/dist/utils/permissions.d.ts.map +1 -0
- package/dist/utils/permissions.js +93 -0
- package/dist/utils/stringHelpers.d.ts +17 -0
- package/dist/utils/stringHelpers.d.ts.map +1 -0
- package/dist/utils/stringHelpers.js +38 -0
- package/dist/utils/transitions.d.ts +99 -1
- package/dist/utils/transitions.d.ts.map +1 -1
- package/dist/utils/transitions.js +144 -2
- package/dist/utils/utils/utils.d.ts +2 -73
- package/dist/utils/utils/utils.d.ts.map +1 -1
- package/dist/utils/utils/utils.js +2 -2
- package/dist/utils/utils.d.ts +41 -98
- package/dist/utils/utils.d.ts.map +1 -1
- package/dist/utils/utils.js +58 -701
- package/package.json +16 -3
- package/dist/services/EventService.d.ts +0 -5
- package/dist/services/EventService.d.ts.map +0 -1
- package/dist/services/EventService.js +0 -79
- package/dist/services/EventService.spec.d.ts +0 -2
- package/dist/services/EventService.spec.d.ts.map +0 -1
- package/dist/services/EventService.spec.js +0 -217
- package/dist/services/ShowService.d.ts.map +0 -1
- package/dist/services/ShowService.js +0 -144
- package/dist/services/ShowService.spec.d.ts +0 -2
- package/dist/services/ShowService.spec.d.ts.map +0 -1
- package/dist/services/ShowService.spec.js +0 -345
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { API_BASE_URL, USE_V2_API, buildApiUrl, buildPerformerInviteUrl, getPerformerInviteMethod } from "../utils/apiConfig";
|
|
2
|
+
import { getPerformerToken } from "../utils/utils";
|
|
3
|
+
/**
|
|
4
|
+
* Accept an event performer invitation
|
|
5
|
+
*/
|
|
6
|
+
export async function acceptInvite(inviteId, message = "") {
|
|
7
|
+
try {
|
|
8
|
+
const response = await fetch(buildPerformerInviteUrl(inviteId, "accept"), {
|
|
9
|
+
method: getPerformerInviteMethod("accept"),
|
|
10
|
+
headers: {
|
|
11
|
+
"Content-Type": "application/json",
|
|
12
|
+
},
|
|
13
|
+
body: JSON.stringify({ message }),
|
|
14
|
+
});
|
|
15
|
+
if (response.ok) {
|
|
16
|
+
const result = await response.json();
|
|
17
|
+
return { ok: true, result };
|
|
18
|
+
}
|
|
19
|
+
return { ok: false };
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
console.error("Error accepting invite:", error);
|
|
23
|
+
return { ok: false };
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Decline an event performer invitation
|
|
28
|
+
*/
|
|
29
|
+
export async function declineInvite(inviteId, message = "") {
|
|
30
|
+
try {
|
|
31
|
+
const response = await fetch(buildPerformerInviteUrl(inviteId, "decline"), {
|
|
32
|
+
method: getPerformerInviteMethod("decline"),
|
|
33
|
+
headers: {
|
|
34
|
+
"Content-Type": "application/json",
|
|
35
|
+
},
|
|
36
|
+
body: JSON.stringify({ message }),
|
|
37
|
+
});
|
|
38
|
+
if (response.ok) {
|
|
39
|
+
const result = await response.json();
|
|
40
|
+
return { ok: true, result };
|
|
41
|
+
}
|
|
42
|
+
return { ok: false };
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
console.error("Error declining invite:", error);
|
|
46
|
+
return { ok: false };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Cancel an event performer invitation
|
|
51
|
+
*/
|
|
52
|
+
export async function cancelInvite(inviteId, message = "") {
|
|
53
|
+
try {
|
|
54
|
+
const response = await fetch(buildPerformerInviteUrl(inviteId, "cancel"), {
|
|
55
|
+
method: getPerformerInviteMethod("cancel"),
|
|
56
|
+
headers: {
|
|
57
|
+
"Content-Type": "application/json",
|
|
58
|
+
},
|
|
59
|
+
body: JSON.stringify({ message }),
|
|
60
|
+
});
|
|
61
|
+
if (response.ok) {
|
|
62
|
+
const result = await response.json();
|
|
63
|
+
return { ok: true, result };
|
|
64
|
+
}
|
|
65
|
+
return { ok: false };
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
console.error("Error cancelling invite:", error);
|
|
69
|
+
return { ok: false };
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Send a message to a venue
|
|
74
|
+
*/
|
|
75
|
+
export async function sendVenueMessage(inviteId, message) {
|
|
76
|
+
try {
|
|
77
|
+
const token = getPerformerToken();
|
|
78
|
+
const url = USE_V2_API
|
|
79
|
+
? buildApiUrl(`/api/v2/performer/invites/${inviteId}/message`)
|
|
80
|
+
: buildApiUrl(`/api/performer/sendVenueMessage/${inviteId}`);
|
|
81
|
+
const response = await fetch(url, {
|
|
82
|
+
method: "POST",
|
|
83
|
+
headers: {
|
|
84
|
+
"Content-Type": "application/json",
|
|
85
|
+
Authorization: `Bearer ${token}`,
|
|
86
|
+
},
|
|
87
|
+
credentials: "include",
|
|
88
|
+
body: JSON.stringify({ message }),
|
|
89
|
+
});
|
|
90
|
+
if (response.ok) {
|
|
91
|
+
const text = await response.text();
|
|
92
|
+
let result = { message: "Email sent successfully" };
|
|
93
|
+
if (text) {
|
|
94
|
+
try {
|
|
95
|
+
result = JSON.parse(text);
|
|
96
|
+
}
|
|
97
|
+
catch (_e) {
|
|
98
|
+
// Empty response is fine
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return { ok: true, result };
|
|
102
|
+
}
|
|
103
|
+
return { ok: false };
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
console.error("Error sending venue message:", error);
|
|
107
|
+
return { ok: false };
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Get the ticketing event URL
|
|
112
|
+
*/
|
|
113
|
+
export function getEventUrl(venueId) {
|
|
114
|
+
return `${API_BASE_URL}/ticketing/events/${venueId}`;
|
|
115
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"show.service.spec.d.ts","sourceRoot":"","sources":["../../src/lib/services/show.service.spec.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
// Mock the utility modules before imports
|
|
3
|
+
vi.mock('$lib/utils/apiConfig', () => ({
|
|
4
|
+
API_BASE_URL: 'https://get-micdrop.com',
|
|
5
|
+
USE_V2_API: false,
|
|
6
|
+
buildApiUrl: (path) => {
|
|
7
|
+
const cleanPath = path.startsWith('/') ? path : `/${path}`;
|
|
8
|
+
return `https://get-micdrop.com${cleanPath}`;
|
|
9
|
+
},
|
|
10
|
+
buildPerformerInviteUrl: (inviteId, action) => {
|
|
11
|
+
const v2Action = action === 'accept' ? 'confirm' : action;
|
|
12
|
+
return `https://get-micdrop.com/api/v2/event-performers/${inviteId}/${v2Action}`;
|
|
13
|
+
},
|
|
14
|
+
getPerformerInviteMethod: (action) => action === 'cancel' ? 'PUT' : 'POST',
|
|
15
|
+
}));
|
|
16
|
+
vi.mock('$lib/utils/utils', () => ({
|
|
17
|
+
getPerformerToken: () => 'test-token',
|
|
18
|
+
}));
|
|
19
|
+
import { acceptInvite, declineInvite, cancelInvite, sendVenueMessage, getEventUrl, } from './show.service';
|
|
20
|
+
describe('ShowService', () => {
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
vi.clearAllMocks();
|
|
23
|
+
global.fetch = vi.fn();
|
|
24
|
+
});
|
|
25
|
+
describe('acceptInvite', () => {
|
|
26
|
+
it('makes PUT request to accept invite endpoint', async () => {
|
|
27
|
+
global.fetch.mockResolvedValue({
|
|
28
|
+
ok: true,
|
|
29
|
+
json: () => Promise.resolve({ success: true }),
|
|
30
|
+
});
|
|
31
|
+
await acceptInvite('123');
|
|
32
|
+
expect(global.fetch).toHaveBeenCalledWith('https://get-micdrop.com/api/v2/event-performers/123/confirm', expect.objectContaining({
|
|
33
|
+
method: 'POST',
|
|
34
|
+
headers: { 'Content-Type': 'application/json' },
|
|
35
|
+
}));
|
|
36
|
+
});
|
|
37
|
+
it('returns ok: true on successful response', async () => {
|
|
38
|
+
global.fetch.mockResolvedValue({
|
|
39
|
+
ok: true,
|
|
40
|
+
json: () => Promise.resolve({ id: 123 }),
|
|
41
|
+
});
|
|
42
|
+
const result = await acceptInvite('123');
|
|
43
|
+
expect(result.ok).toBe(true);
|
|
44
|
+
expect(result.result).toEqual({ id: 123 });
|
|
45
|
+
});
|
|
46
|
+
it('returns ok: false on failed response', async () => {
|
|
47
|
+
global.fetch.mockResolvedValue({
|
|
48
|
+
ok: false,
|
|
49
|
+
});
|
|
50
|
+
const result = await acceptInvite('123');
|
|
51
|
+
expect(result.ok).toBe(false);
|
|
52
|
+
});
|
|
53
|
+
it('returns ok: false on network error', async () => {
|
|
54
|
+
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
|
|
55
|
+
global.fetch.mockRejectedValue(new Error('Network error'));
|
|
56
|
+
const result = await acceptInvite('123');
|
|
57
|
+
expect(result.ok).toBe(false);
|
|
58
|
+
consoleSpy.mockRestore();
|
|
59
|
+
});
|
|
60
|
+
it('includes message in request body', async () => {
|
|
61
|
+
global.fetch.mockResolvedValue({
|
|
62
|
+
ok: true,
|
|
63
|
+
json: () => Promise.resolve({}),
|
|
64
|
+
});
|
|
65
|
+
await acceptInvite('123', 'Thanks for the invite!');
|
|
66
|
+
expect(global.fetch).toHaveBeenCalledWith(expect.any(String), expect.objectContaining({
|
|
67
|
+
body: JSON.stringify({ message: 'Thanks for the invite!' }),
|
|
68
|
+
}));
|
|
69
|
+
});
|
|
70
|
+
it('sends empty message by default', async () => {
|
|
71
|
+
global.fetch.mockResolvedValue({
|
|
72
|
+
ok: true,
|
|
73
|
+
json: () => Promise.resolve({}),
|
|
74
|
+
});
|
|
75
|
+
await acceptInvite('123');
|
|
76
|
+
expect(global.fetch).toHaveBeenCalledWith(expect.any(String), expect.objectContaining({
|
|
77
|
+
body: JSON.stringify({ message: '' }),
|
|
78
|
+
}));
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
describe('declineInvite', () => {
|
|
82
|
+
it('makes PUT request to decline invite endpoint', async () => {
|
|
83
|
+
global.fetch.mockResolvedValue({
|
|
84
|
+
ok: true,
|
|
85
|
+
json: () => Promise.resolve({}),
|
|
86
|
+
});
|
|
87
|
+
await declineInvite('456');
|
|
88
|
+
expect(global.fetch).toHaveBeenCalledWith('https://get-micdrop.com/api/v2/event-performers/456/decline', expect.objectContaining({
|
|
89
|
+
method: 'POST',
|
|
90
|
+
}));
|
|
91
|
+
});
|
|
92
|
+
it('returns ok: true on success', async () => {
|
|
93
|
+
global.fetch.mockResolvedValue({
|
|
94
|
+
ok: true,
|
|
95
|
+
json: () => Promise.resolve({ declined: true }),
|
|
96
|
+
});
|
|
97
|
+
const result = await declineInvite('456');
|
|
98
|
+
expect(result.ok).toBe(true);
|
|
99
|
+
expect(result.result).toEqual({ declined: true });
|
|
100
|
+
});
|
|
101
|
+
it('returns ok: false on failure', async () => {
|
|
102
|
+
global.fetch.mockResolvedValue({
|
|
103
|
+
ok: false,
|
|
104
|
+
});
|
|
105
|
+
const result = await declineInvite('456');
|
|
106
|
+
expect(result.ok).toBe(false);
|
|
107
|
+
});
|
|
108
|
+
it('returns ok: false on error', async () => {
|
|
109
|
+
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
|
|
110
|
+
global.fetch.mockRejectedValue(new Error('Error'));
|
|
111
|
+
const result = await declineInvite('456');
|
|
112
|
+
expect(result.ok).toBe(false);
|
|
113
|
+
consoleSpy.mockRestore();
|
|
114
|
+
});
|
|
115
|
+
it('includes decline message in body', async () => {
|
|
116
|
+
global.fetch.mockResolvedValue({
|
|
117
|
+
ok: true,
|
|
118
|
+
json: () => Promise.resolve({}),
|
|
119
|
+
});
|
|
120
|
+
await declineInvite('456', 'Schedule conflict');
|
|
121
|
+
expect(global.fetch).toHaveBeenCalledWith(expect.any(String), expect.objectContaining({
|
|
122
|
+
body: JSON.stringify({ message: 'Schedule conflict' }),
|
|
123
|
+
}));
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
describe('cancelInvite', () => {
|
|
127
|
+
it('makes PUT request to cancel invite endpoint', async () => {
|
|
128
|
+
global.fetch.mockResolvedValue({
|
|
129
|
+
ok: true,
|
|
130
|
+
json: () => Promise.resolve({}),
|
|
131
|
+
});
|
|
132
|
+
await cancelInvite('789');
|
|
133
|
+
expect(global.fetch).toHaveBeenCalledWith('https://get-micdrop.com/api/v2/event-performers/789/cancel', expect.objectContaining({
|
|
134
|
+
method: 'PUT',
|
|
135
|
+
}));
|
|
136
|
+
});
|
|
137
|
+
it('returns ok: true on success', async () => {
|
|
138
|
+
global.fetch.mockResolvedValue({
|
|
139
|
+
ok: true,
|
|
140
|
+
json: () => Promise.resolve({ cancelled: true }),
|
|
141
|
+
});
|
|
142
|
+
const result = await cancelInvite('789');
|
|
143
|
+
expect(result.ok).toBe(true);
|
|
144
|
+
});
|
|
145
|
+
it('returns ok: false on failure', async () => {
|
|
146
|
+
global.fetch.mockResolvedValue({
|
|
147
|
+
ok: false,
|
|
148
|
+
});
|
|
149
|
+
const result = await cancelInvite('789');
|
|
150
|
+
expect(result.ok).toBe(false);
|
|
151
|
+
});
|
|
152
|
+
it('returns ok: false on error', async () => {
|
|
153
|
+
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
|
|
154
|
+
global.fetch.mockRejectedValue(new Error('Error'));
|
|
155
|
+
const result = await cancelInvite('789');
|
|
156
|
+
expect(result.ok).toBe(false);
|
|
157
|
+
consoleSpy.mockRestore();
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
describe('sendVenueMessage', () => {
|
|
161
|
+
it('makes POST request with auth token', async () => {
|
|
162
|
+
global.fetch.mockResolvedValue({
|
|
163
|
+
ok: true,
|
|
164
|
+
text: () => Promise.resolve(''),
|
|
165
|
+
});
|
|
166
|
+
await sendVenueMessage('123', 'Hello!');
|
|
167
|
+
expect(global.fetch).toHaveBeenCalledWith('https://get-micdrop.com/api/performer/sendVenueMessage/123', expect.objectContaining({
|
|
168
|
+
method: 'POST',
|
|
169
|
+
headers: {
|
|
170
|
+
'Content-Type': 'application/json',
|
|
171
|
+
Authorization: 'Bearer test-token',
|
|
172
|
+
},
|
|
173
|
+
credentials: 'include',
|
|
174
|
+
}));
|
|
175
|
+
});
|
|
176
|
+
it('sends message in request body', async () => {
|
|
177
|
+
global.fetch.mockResolvedValue({
|
|
178
|
+
ok: true,
|
|
179
|
+
text: () => Promise.resolve(''),
|
|
180
|
+
});
|
|
181
|
+
await sendVenueMessage('123', 'Hello venue!');
|
|
182
|
+
expect(global.fetch).toHaveBeenCalledWith(expect.any(String), expect.objectContaining({
|
|
183
|
+
body: JSON.stringify({ message: 'Hello venue!' }),
|
|
184
|
+
}));
|
|
185
|
+
});
|
|
186
|
+
it('returns ok: true with default message on empty response', async () => {
|
|
187
|
+
global.fetch.mockResolvedValue({
|
|
188
|
+
ok: true,
|
|
189
|
+
text: () => Promise.resolve(''),
|
|
190
|
+
});
|
|
191
|
+
const result = await sendVenueMessage('123', 'Hello');
|
|
192
|
+
expect(result.ok).toBe(true);
|
|
193
|
+
expect(result.result).toEqual({ message: 'Email sent successfully' });
|
|
194
|
+
});
|
|
195
|
+
it('parses JSON response when present', async () => {
|
|
196
|
+
global.fetch.mockResolvedValue({
|
|
197
|
+
ok: true,
|
|
198
|
+
text: () => Promise.resolve(JSON.stringify({ sent: true, id: 456 })),
|
|
199
|
+
});
|
|
200
|
+
const result = await sendVenueMessage('123', 'Hello');
|
|
201
|
+
expect(result.ok).toBe(true);
|
|
202
|
+
expect(result.result).toEqual({ sent: true, id: 456 });
|
|
203
|
+
});
|
|
204
|
+
it('handles invalid JSON in response gracefully', async () => {
|
|
205
|
+
global.fetch.mockResolvedValue({
|
|
206
|
+
ok: true,
|
|
207
|
+
text: () => Promise.resolve('not json'),
|
|
208
|
+
});
|
|
209
|
+
const result = await sendVenueMessage('123', 'Hello');
|
|
210
|
+
expect(result.ok).toBe(true);
|
|
211
|
+
expect(result.result).toEqual({ message: 'Email sent successfully' });
|
|
212
|
+
});
|
|
213
|
+
it('returns ok: false on failure', async () => {
|
|
214
|
+
global.fetch.mockResolvedValue({
|
|
215
|
+
ok: false,
|
|
216
|
+
});
|
|
217
|
+
const result = await sendVenueMessage('123', 'Hello');
|
|
218
|
+
expect(result.ok).toBe(false);
|
|
219
|
+
});
|
|
220
|
+
it('returns ok: false on error', async () => {
|
|
221
|
+
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
|
|
222
|
+
global.fetch.mockRejectedValue(new Error('Network error'));
|
|
223
|
+
const result = await sendVenueMessage('123', 'Hello');
|
|
224
|
+
expect(result.ok).toBe(false);
|
|
225
|
+
consoleSpy.mockRestore();
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
describe('getEventUrl', () => {
|
|
229
|
+
it('returns ticketing URL for venue', () => {
|
|
230
|
+
const url = getEventUrl('123');
|
|
231
|
+
expect(url).toBe('https://get-micdrop.com/ticketing/events/123');
|
|
232
|
+
});
|
|
233
|
+
it('handles numeric venue ID', () => {
|
|
234
|
+
const url = getEventUrl(456);
|
|
235
|
+
expect(url).toBe('https://get-micdrop.com/ticketing/events/456');
|
|
236
|
+
});
|
|
237
|
+
it('handles string venue ID', () => {
|
|
238
|
+
const url = getEventUrl('venue-abc');
|
|
239
|
+
expect(url).toBe('https://get-micdrop.com/ticketing/events/venue-abc');
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
});
|
package/dist/tailwind/preset.cjs
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
const forms = require('@tailwindcss/forms');
|
|
2
|
+
|
|
1
3
|
const colors = {
|
|
2
4
|
'bg-primary': 'hsl(var(--bg-primary) / <alpha-value>)',
|
|
3
5
|
'bg-secondary': 'hsl(var(--bg-secondary) / <alpha-value>)',
|
|
@@ -79,4 +81,7 @@ module.exports = {
|
|
|
79
81
|
},
|
|
80
82
|
},
|
|
81
83
|
},
|
|
84
|
+
// @tailwindcss/forms is included here so consuming apps get it via the preset.
|
|
85
|
+
// Consuming apps should NOT also @plugin "@tailwindcss/forms" to avoid double-loading.
|
|
86
|
+
plugins: [forms],
|
|
82
87
|
};
|
|
@@ -62,6 +62,7 @@ declare const zIndex: {
|
|
|
62
62
|
70: string;
|
|
63
63
|
100: string;
|
|
64
64
|
};
|
|
65
|
+
import forms = require("@tailwindcss/forms");
|
|
65
66
|
export namespace theme {
|
|
66
67
|
namespace extend {
|
|
67
68
|
export { colors };
|
|
@@ -74,5 +75,6 @@ export namespace theme {
|
|
|
74
75
|
}
|
|
75
76
|
}
|
|
76
77
|
}
|
|
78
|
+
export let plugins: (typeof forms)[];
|
|
77
79
|
export {};
|
|
78
80
|
//# sourceMappingURL=preset.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preset.d.cts","sourceRoot":"","sources":["../../../../src/lib/tailwind/preset.cjs"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"preset.d.cts","sourceRoot":"","sources":["../../../../src/lib/tailwind/preset.cjs"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4BE;AAEF;;;;;;;;;;EAUE;AAEF;;;;;;;;;;EAUE;AAEF;;;;;;;;;;;;EAYE"}
|
|
@@ -62,8 +62,8 @@ describe('spacing tokens', () => {
|
|
|
62
62
|
expect(gapScale.semantic.layout).toBe('gap-8');
|
|
63
63
|
expect(gapScale.semantic.page).toBe('gap-12');
|
|
64
64
|
});
|
|
65
|
-
it('has
|
|
66
|
-
expect(Object.keys(gapScale.semantic)).toHaveLength(
|
|
65
|
+
it('has 12 semantic gap values', () => {
|
|
66
|
+
expect(Object.keys(gapScale.semantic)).toHaveLength(12);
|
|
67
67
|
});
|
|
68
68
|
});
|
|
69
69
|
describe('numeric', () => {
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/* ==========================================================================
|
|
2
|
+
BASE RESETS
|
|
3
|
+
Shared form resets, accessibility utilities, and platform fixes.
|
|
4
|
+
Imported via base.css -- every Micdrop app gets these automatically.
|
|
5
|
+
|
|
6
|
+
IMPORTANT: Do NOT add `appearance: none` to inputs here.
|
|
7
|
+
@tailwindcss/forms handles form element appearance. Setting it globally
|
|
8
|
+
breaks native checkboxes and radios (see MIC-859).
|
|
9
|
+
========================================================================== */
|
|
10
|
+
|
|
11
|
+
/* --------------------------------------------------------------------------
|
|
12
|
+
iOS ZOOM PREVENTION
|
|
13
|
+
iOS Safari auto-zooms when focusing inputs with font-size < 16px.
|
|
14
|
+
Set minimum 16px on text inputs only. Exclude checkbox/radio which
|
|
15
|
+
don't trigger zoom and need their native appearance preserved.
|
|
16
|
+
-------------------------------------------------------------------------- */
|
|
17
|
+
input:not([type="checkbox"]):not([type="radio"]),
|
|
18
|
+
textarea,
|
|
19
|
+
select {
|
|
20
|
+
font-size: 16px;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/* --------------------------------------------------------------------------
|
|
24
|
+
FORM FONT INHERITANCE
|
|
25
|
+
Form elements don't inherit font by default in most browsers.
|
|
26
|
+
-------------------------------------------------------------------------- */
|
|
27
|
+
input,
|
|
28
|
+
button {
|
|
29
|
+
font-size: inherit;
|
|
30
|
+
font-family: inherit;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/* --------------------------------------------------------------------------
|
|
34
|
+
FOCUS MANAGEMENT
|
|
35
|
+
Hide focus ring on mouse/touch interaction, show on keyboard navigation.
|
|
36
|
+
This provides clean UI for mouse users while maintaining accessibility
|
|
37
|
+
for keyboard users.
|
|
38
|
+
-------------------------------------------------------------------------- */
|
|
39
|
+
button:focus:not(:focus-visible) {
|
|
40
|
+
outline: none;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/* --------------------------------------------------------------------------
|
|
44
|
+
SCREEN READER ONLY
|
|
45
|
+
Visually hides content while keeping it accessible to screen readers.
|
|
46
|
+
Standard pattern matching Tailwind's built-in sr-only utility.
|
|
47
|
+
This is a fallback for non-Tailwind contexts -- Tailwind's version
|
|
48
|
+
will override it due to CSS specificity/source order.
|
|
49
|
+
-------------------------------------------------------------------------- */
|
|
50
|
+
.sr-only {
|
|
51
|
+
position: absolute;
|
|
52
|
+
width: 1px;
|
|
53
|
+
height: 1px;
|
|
54
|
+
padding: 0;
|
|
55
|
+
margin: -1px;
|
|
56
|
+
overflow: hidden;
|
|
57
|
+
clip: rect(0, 0, 0, 0);
|
|
58
|
+
white-space: nowrap;
|
|
59
|
+
border-width: 0;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/* --------------------------------------------------------------------------
|
|
63
|
+
SCREEN READER ONLY - FOCUSABLE
|
|
64
|
+
Makes sr-only content visible when focused. Enables skip-navigation
|
|
65
|
+
links and other focus-activated accessibility patterns.
|
|
66
|
+
-------------------------------------------------------------------------- */
|
|
67
|
+
.sr-only-focusable:focus,
|
|
68
|
+
.sr-only-focusable:active {
|
|
69
|
+
position: static;
|
|
70
|
+
width: auto;
|
|
71
|
+
height: auto;
|
|
72
|
+
padding: 0;
|
|
73
|
+
margin: 0;
|
|
74
|
+
overflow: visible;
|
|
75
|
+
clip: auto;
|
|
76
|
+
white-space: normal;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/* --------------------------------------------------------------------------
|
|
80
|
+
REDUCED MOTION
|
|
81
|
+
Respects user's motion preference. Disables all animations and
|
|
82
|
+
transitions when prefers-reduced-motion is set.
|
|
83
|
+
-------------------------------------------------------------------------- */
|
|
84
|
+
@media (prefers-reduced-motion: reduce) {
|
|
85
|
+
*,
|
|
86
|
+
*::before,
|
|
87
|
+
*::after {
|
|
88
|
+
animation-duration: 0.01ms !important;
|
|
89
|
+
animation-iteration-count: 1 !important;
|
|
90
|
+
transition-duration: 0.01ms !important;
|
|
91
|
+
scroll-behavior: auto !important;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/* --------------------------------------------------------------------------
|
|
96
|
+
CHROME AUTOFILL DARK MODE FIX
|
|
97
|
+
Chrome's autofill sets a hardcoded light background on inputs.
|
|
98
|
+
In dark mode, this creates a jarring white flash. Override with
|
|
99
|
+
the app's dark background color.
|
|
100
|
+
-------------------------------------------------------------------------- */
|
|
101
|
+
.dark input:-webkit-autofill,
|
|
102
|
+
.dark input:-webkit-autofill:hover,
|
|
103
|
+
.dark input:-webkit-autofill:focus,
|
|
104
|
+
.dark textarea:-webkit-autofill,
|
|
105
|
+
.dark textarea:-webkit-autofill:hover,
|
|
106
|
+
.dark textarea:-webkit-autofill:focus {
|
|
107
|
+
-webkit-box-shadow: 0 0 0 1000px hsl(var(--bg-primary)) inset !important;
|
|
108
|
+
-webkit-text-fill-color: hsl(var(--text-primary)) !important;
|
|
109
|
+
background-color: hsl(var(--bg-primary)) !important;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/* --------------------------------------------------------------------------
|
|
113
|
+
DESKTOP FONT SCALING
|
|
114
|
+
110% base font on desktop makes all rem-based sizing 10% larger:
|
|
115
|
+
- Buttons: 44px -> 48px (exceeds WCAG AAA 44px target)
|
|
116
|
+
- Text: More readable without zooming
|
|
117
|
+
- Touch targets: More accessible
|
|
118
|
+
Mobile stays at 100% (already optimized for touch).
|
|
119
|
+
-------------------------------------------------------------------------- */
|
|
120
|
+
@media (min-width: 768px) {
|
|
121
|
+
html {
|
|
122
|
+
font-size: 110%;
|
|
123
|
+
}
|
|
124
|
+
}
|
package/dist/tokens/spacing.d.ts
CHANGED
|
@@ -71,6 +71,7 @@ export declare const gapScale: {
|
|
|
71
71
|
readonly group: "gap-3";
|
|
72
72
|
readonly tight: "gap-4";
|
|
73
73
|
readonly content: "gap-4";
|
|
74
|
+
readonly card: "gap-5";
|
|
74
75
|
readonly standard: "gap-6";
|
|
75
76
|
readonly section: "gap-6";
|
|
76
77
|
readonly spacious: "gap-8";
|
|
@@ -114,6 +115,7 @@ export declare const gapMap: {
|
|
|
114
115
|
readonly group: "gap-3";
|
|
115
116
|
readonly tight: "gap-4";
|
|
116
117
|
readonly content: "gap-4";
|
|
118
|
+
readonly card: "gap-5";
|
|
117
119
|
readonly standard: "gap-6";
|
|
118
120
|
readonly section: "gap-6";
|
|
119
121
|
readonly spacious: "gap-8";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spacing.d.ts","sourceRoot":"","sources":["../../src/lib/tokens/spacing.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoCV,CAAC;AAEX,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;CAiBjB,CAAC;AAEX,MAAM,MAAM,UAAU,GAAG,MAAM,OAAO,OAAO,CAAC;AAC9C,MAAM,MAAM,YAAY,GAAG,MAAM,OAAO,cAAc,CAAC;AAEvD;;;GAGG;AACH,eAAO,MAAM,QAAQ
|
|
1
|
+
{"version":3,"file":"spacing.d.ts","sourceRoot":"","sources":["../../src/lib/tokens/spacing.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoCV,CAAC;AAEX,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;CAiBjB,CAAC;AAEX,MAAM,MAAM,UAAU,GAAG,MAAM,OAAO,OAAO,CAAC;AAC9C,MAAM,MAAM,YAAY,GAAG,MAAM,OAAO,cAAc,CAAC;AAEvD;;;GAGG;AACH,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4CX,CAAC;AAEX,yCAAyC;AACzC,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAKT,CAAC;AAEX,MAAM,MAAM,cAAc,GAAG,MAAM,OAAO,QAAQ,CAAC,QAAQ,CAAC;AAC5D,MAAM,MAAM,aAAa,GAAG,MAAM,OAAO,QAAQ,CAAC,OAAO,CAAC;AAC1D,MAAM,MAAM,YAAY,GAAG,MAAM,OAAO,QAAQ,CAAC,MAAM,CAAC;AACxD,MAAM,MAAM,UAAU,GAAG,MAAM,OAAO,QAAQ,CAAC,IAAI,CAAC;AACpD,MAAM,MAAM,MAAM,GAAG,MAAM,OAAO,MAAM,CAAC;AAEzC;;GAEG;AACH,eAAO,MAAM,UAAU;;;;;;CAMb,CAAC;AAEX,MAAM,MAAM,QAAQ,GAAG,MAAM,OAAO,UAAU,CAAC;AAE/C;;GAEG;AACH,eAAO,MAAM,YAAY;;;;;;;CAOf,CAAC;AAEX,MAAM,MAAM,UAAU,GAAG,MAAM,OAAO,YAAY,CAAC"}
|
package/dist/tokens/spacing.js
CHANGED
|
@@ -70,6 +70,7 @@ export const gapScale = {
|
|
|
70
70
|
group: 'gap-3', // 12px - Between related elements
|
|
71
71
|
tight: 'gap-4', // 16px - Tight grid/content spacing
|
|
72
72
|
content: 'gap-4', // 16px - Default content spacing
|
|
73
|
+
card: 'gap-5', // 20px - Card internal spacing
|
|
73
74
|
standard: 'gap-6', // 24px - Standard grid spacing
|
|
74
75
|
section: 'gap-6', // 24px - Between sections
|
|
75
76
|
spacious: 'gap-8', // 32px - Spacious grid items
|
package/dist/tokens/tokens.css
CHANGED
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
--Text-Primary: var(--text-primary);
|
|
41
41
|
--Text-Secondary: var(--text-secondary);
|
|
42
42
|
--Text-Tartiary: var(--text-tertiary);
|
|
43
|
+
--Text-Tertiary: var(--text-tertiary);
|
|
43
44
|
--Text-Head: var(--text-head);
|
|
44
45
|
--Brand-Primary: var(--brand-primary);
|
|
45
46
|
--Accent-Warning: var(--accent-warning);
|
|
@@ -49,7 +50,6 @@
|
|
|
49
50
|
--Stroke-Secondary: var(--stroke-secondary);
|
|
50
51
|
}
|
|
51
52
|
|
|
52
|
-
[data-theme="dark"],
|
|
53
53
|
.dark {
|
|
54
54
|
--bg-primary: 220 20% 18%;
|
|
55
55
|
--bg-secondary: 221 50% 10%;
|
|
@@ -26,9 +26,9 @@
|
|
|
26
26
|
* Common flex patterns for consistent layouts
|
|
27
27
|
* ========================================================================== */
|
|
28
28
|
|
|
29
|
-
/*
|
|
29
|
+
/* Flex row with vertically centered items (base for flex-center-* variants) */
|
|
30
30
|
.flex-center {
|
|
31
|
-
@apply flex items-center
|
|
31
|
+
@apply flex items-center;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
/* Inline flex centered */
|
|
@@ -350,4 +350,81 @@
|
|
|
350
350
|
.desktop-flex {
|
|
351
351
|
@apply hidden md:flex;
|
|
352
352
|
}
|
|
353
|
+
|
|
354
|
+
/* ==========================================================================
|
|
355
|
+
* ANIMATION UTILITIES
|
|
356
|
+
* Shared animations used across multiple Micdrop apps
|
|
357
|
+
* ========================================================================== */
|
|
358
|
+
|
|
359
|
+
/* Shake animation - used in verify-phone and login flows */
|
|
360
|
+
.shake {
|
|
361
|
+
animation: shake 0.625s linear;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/* Skeleton loading - shimmer effect for loading placeholders */
|
|
365
|
+
.skeleton-loading {
|
|
366
|
+
animation: skeleton-shimmer 2.5s ease-in-out infinite;
|
|
367
|
+
background: linear-gradient(
|
|
368
|
+
90deg,
|
|
369
|
+
#e5e7eb 0%,
|
|
370
|
+
#e5e7eb 40%,
|
|
371
|
+
#f3f4f6 50%,
|
|
372
|
+
#e5e7eb 60%,
|
|
373
|
+
#e5e7eb 100%
|
|
374
|
+
);
|
|
375
|
+
background-size: 200% 100%;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
.dark .skeleton-loading {
|
|
379
|
+
background: linear-gradient(
|
|
380
|
+
90deg,
|
|
381
|
+
#374151 0%,
|
|
382
|
+
#374151 40%,
|
|
383
|
+
#4b5563 50%,
|
|
384
|
+
#374151 60%,
|
|
385
|
+
#374151 100%
|
|
386
|
+
);
|
|
387
|
+
background-size: 200% 100%;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/* Animation keyframes - placed outside @layer for browser compatibility */
|
|
392
|
+
@keyframes shake {
|
|
393
|
+
8%,
|
|
394
|
+
41% {
|
|
395
|
+
transform: translateX(-10px);
|
|
396
|
+
}
|
|
397
|
+
25%,
|
|
398
|
+
58% {
|
|
399
|
+
transform: translateX(10px);
|
|
400
|
+
}
|
|
401
|
+
75% {
|
|
402
|
+
transform: translateX(-5px);
|
|
403
|
+
}
|
|
404
|
+
92% {
|
|
405
|
+
transform: translateX(5px);
|
|
406
|
+
}
|
|
407
|
+
0%,
|
|
408
|
+
100% {
|
|
409
|
+
transform: translateX(0);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
@keyframes skeleton-shimmer {
|
|
414
|
+
0% {
|
|
415
|
+
background-position: -200% 0;
|
|
416
|
+
}
|
|
417
|
+
100% {
|
|
418
|
+
background-position: 200% 0;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
@keyframes skeleton-pulse {
|
|
423
|
+
0%,
|
|
424
|
+
100% {
|
|
425
|
+
opacity: 1;
|
|
426
|
+
}
|
|
427
|
+
50% {
|
|
428
|
+
opacity: 0.5;
|
|
429
|
+
}
|
|
353
430
|
}
|