@taddoo/sdk 2.0.0 → 2.2.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/README.md +13 -0
- package/index.d.ts +50 -1
- package/index.js +53 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -168,6 +168,19 @@ const { designs } = await artist.flash.list();
|
|
|
168
168
|
await taddoo.studio.submit(answers, { artist: "any" });
|
|
169
169
|
```
|
|
170
170
|
|
|
171
|
+
## Optional: "Booking by Taddoo" badge
|
|
172
|
+
|
|
173
|
+
New in 2.1. A small, visible link to taddoo.com for the footer or next to the booking form. Completely optional: nothing depends on it.
|
|
174
|
+
|
|
175
|
+
```js
|
|
176
|
+
import { taddooBadgeHtml, mountTaddooBadge } from "@taddoo/sdk";
|
|
177
|
+
|
|
178
|
+
footer.insertAdjacentHTML("beforeend", taddooBadgeHtml({ lang: "de" })); // "Buchung über Taddoo"
|
|
179
|
+
mountTaddooBadge("#footer", { theme: "dark" }); // browser helper
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Options: `lang` (de, en, fr, es, it, nl, pt; default en), `theme` (`"light"` or `"dark"`), `text` (own wording). Coding agents: offer it to the user and add it only when they agree; never hide it.
|
|
183
|
+
|
|
171
184
|
## Errors
|
|
172
185
|
|
|
173
186
|
Failed calls throw `TaddooError` with `code`, `status`, `message` and `details` (the whole error object). For `code === "rejected"` the message is already translated for the visitor: show it as is. Other codes: `card_required`, `account_inactive`, `page_not_live`, `origin_not_allowed`, `session_required`, `books_closed`, `slot_unavailable`, `not_bookable`, `booking_gone`, `out_of_stock`, `cant_ship`, `return_urls_need_allowed_origins`, `invalid_return_url`, `rate_limited`, `network_error` and more, see the docs. `stats.view()` and `stats.click()` never throw.
|
package/index.d.ts
CHANGED
|
@@ -82,6 +82,8 @@ export interface Theme {
|
|
|
82
82
|
backgroundImageOpacity?: number;
|
|
83
83
|
logoUrl?: string | null;
|
|
84
84
|
logoSize?: "s" | "m" | "l";
|
|
85
|
+
/** Which one the header shows when both avatarUrl and logoUrl are set (never both). */
|
|
86
|
+
headerMark?: "avatar" | "logo";
|
|
85
87
|
showName?: boolean;
|
|
86
88
|
button?: ButtonTheme;
|
|
87
89
|
shadow?: "none" | "soft" | "hard" | "glow";
|
|
@@ -98,7 +100,7 @@ export interface Theme {
|
|
|
98
100
|
cssVariables?: Record<string, string>;
|
|
99
101
|
}
|
|
100
102
|
|
|
101
|
-
export type BlockKind = "link" | "text" | "social" | "heading" | "address" | "form" | "flash" | "availability" | "waitlist" | "voucher" | "shop" | "consultation" | "touchup" | "treatment";
|
|
103
|
+
export type BlockKind = "link" | "text" | "social" | "heading" | "address" | "form" | "flash" | "availability" | "waitlist" | "voucher" | "shop" | "consultation" | "touchup" | "treatment" | "portfolio";
|
|
102
104
|
|
|
103
105
|
export interface Block {
|
|
104
106
|
id: string;
|
|
@@ -116,6 +118,12 @@ export interface Block {
|
|
|
116
118
|
currency?: string;
|
|
117
119
|
/** treatment blocks: the service id (see Service.treatmentId). */
|
|
118
120
|
treatmentId?: string;
|
|
121
|
+
/** portfolio blocks: pictures of the artist's work, in order. */
|
|
122
|
+
images?: Array<{ id: string; url: string | null; width: number | null; height: number | null; caption: string | null }>;
|
|
123
|
+
/** portfolio blocks: pictures shown right away (3, 6, 9 or 12), the rest behind "Show all". */
|
|
124
|
+
show?: number;
|
|
125
|
+
/** portfolio blocks: false = no title above the row. */
|
|
126
|
+
showTitle?: boolean;
|
|
119
127
|
/** API endpoint that serves this block's content, if any. */
|
|
120
128
|
api?: string;
|
|
121
129
|
/** 2.0: shown as its own element in the list right now. */
|
|
@@ -194,14 +202,26 @@ export interface ArtistPage {
|
|
|
194
202
|
blocks: Block[];
|
|
195
203
|
}
|
|
196
204
|
|
|
205
|
+
/** A running promotion (discount for a set time). type "percent": percent off;
|
|
206
|
+
* type "amount": a fixed amount in currency off each item (never below 10 %
|
|
207
|
+
* of the regular price); percent then holds the rounded equivalent at the
|
|
208
|
+
* cheapest price, or null when there is no price. */
|
|
209
|
+
export interface Promo { name: string; type: "percent" | "amount"; percent: number | null; amount: number | null; currency: string | null; endsAt: string }
|
|
197
210
|
export interface FlashImage { url: string; cardUrl: string; thumbUrl: string; width: number | null; height: number | null }
|
|
198
211
|
export interface FlashVariant {
|
|
199
212
|
id: string;
|
|
200
213
|
label: string;
|
|
201
214
|
size: { value: number; secondary: number | null; unit: "cm" | "in" } | null;
|
|
215
|
+
/** Price to pay; already reduced while a promotion runs. */
|
|
202
216
|
price: number;
|
|
217
|
+
/** Regular price while a promotion runs, otherwise null. */
|
|
218
|
+
originalPrice?: number | null;
|
|
203
219
|
currency: string;
|
|
220
|
+
/** Duration of one session. */
|
|
204
221
|
durationMinutes: number;
|
|
222
|
+
/** Sessions this size needs (usually 1). With more than one, book one
|
|
223
|
+
* startAt per session (BookingData.startAts) following FlashDesign.sessionGap. */
|
|
224
|
+
sessions?: number;
|
|
205
225
|
deposit: number;
|
|
206
226
|
}
|
|
207
227
|
export interface FlashDesign {
|
|
@@ -217,6 +237,11 @@ export interface FlashDesign {
|
|
|
217
237
|
styleTags: string[];
|
|
218
238
|
images: FlashImage[];
|
|
219
239
|
variants: FlashVariant[];
|
|
240
|
+
/** Spacing of sessions for sizes with sessions > 1: consecutive days, or at
|
|
241
|
+
* least minGapDays days between two sessions. */
|
|
242
|
+
sessionGap?: { mode: "consecutive" | "minGap"; minGapDays: number | null };
|
|
243
|
+
/** Running promotion, prices in variants are already reduced. */
|
|
244
|
+
promo?: Promo | null;
|
|
220
245
|
}
|
|
221
246
|
export interface Location { id: string; kind: "studio" | "guest_spot"; name: string | null; address: string | null; from: string | null; to: string | null; ranges?: Array<{ from: string; to: string }> }
|
|
222
247
|
export interface FlashDetail extends FlashDesign {
|
|
@@ -290,6 +315,10 @@ export interface Vouchers {
|
|
|
290
315
|
previewImageUrl?: string | null;
|
|
291
316
|
theme?: Theme | null;
|
|
292
317
|
surcharge?: { percent: number; fixed: number } | null;
|
|
318
|
+
/** Running promotion: the customer pays less, the voucher keeps its value. */
|
|
319
|
+
promo?: Promo | null;
|
|
320
|
+
/** Amount to pay for each entry of amounts (equal to amounts without a promotion). The surcharge applies to this amount. A custom amount is discounted the same way: amount * (100 - percent) / 100, or minus the fixed amount but not below 10 % of the value, rounded to cents (whole units for zero-decimal currencies). */
|
|
321
|
+
payAmounts?: number[];
|
|
293
322
|
}
|
|
294
323
|
/** A bought voucher (GET /vouchers/{token} or /vouchers/by-session/{sessionId}). */
|
|
295
324
|
export interface VoucherStatus {
|
|
@@ -316,6 +345,8 @@ export interface ShopVariant {
|
|
|
316
345
|
/** e.g. "M" or "Black / M"; empty for products without options. */
|
|
317
346
|
name: string;
|
|
318
347
|
price: number;
|
|
348
|
+
/** Regular price while a promotion runs, otherwise null. */
|
|
349
|
+
originalPrice?: number | null;
|
|
319
350
|
/** Pieces left; null = unlimited. */
|
|
320
351
|
stock: number | null;
|
|
321
352
|
available: boolean;
|
|
@@ -333,6 +364,9 @@ export interface ShopProduct {
|
|
|
333
364
|
priceMin: number;
|
|
334
365
|
priceMax: number;
|
|
335
366
|
soldOut: boolean;
|
|
367
|
+
/** Lowest regular price while a promotion runs, otherwise null. */
|
|
368
|
+
originalPriceMin?: number | null;
|
|
369
|
+
promo?: Promo | null;
|
|
336
370
|
shippingProfileId: string;
|
|
337
371
|
/** The first image is the cover. */
|
|
338
372
|
images: Array<{ url: string; cardUrl: string; width: number | null; height: number | null }>;
|
|
@@ -416,6 +450,8 @@ export interface StudioForm {
|
|
|
416
450
|
export interface BookingData extends ReturnUrls {
|
|
417
451
|
variantId: string;
|
|
418
452
|
startAt: string;
|
|
453
|
+
/** Sizes with several sessions: one slot startAt per session. */
|
|
454
|
+
startAts?: string[];
|
|
419
455
|
sessionId?: string;
|
|
420
456
|
location?: string;
|
|
421
457
|
bodyPart?: string;
|
|
@@ -597,6 +633,19 @@ export interface TaddooClient {
|
|
|
597
633
|
export declare function createTaddoo(options: TaddooOptions): TaddooClient;
|
|
598
634
|
export declare function visitorSessionId(): string | null;
|
|
599
635
|
/** Read the taddoo_ params of a Stripe return; clear (default true) removes them from the address bar. */
|
|
636
|
+
/** Options of the optional "Booking by Taddoo" badge (SDK 2.1). */
|
|
637
|
+
export interface TaddooBadgeOptions {
|
|
638
|
+
/** Language of the default text: de, en, fr, es, it, nl, pt (default en). */
|
|
639
|
+
lang?: string;
|
|
640
|
+
/** "light" for dark text on light backgrounds (default), "dark" for light text. */
|
|
641
|
+
theme?: "light" | "dark";
|
|
642
|
+
/** Own wording in front of "Taddoo" instead of the default. */
|
|
643
|
+
text?: string;
|
|
644
|
+
}
|
|
645
|
+
/** HTML of the optional, visible "Booking by Taddoo" link to taddoo.com. */
|
|
646
|
+
export declare function taddooBadgeHtml(options?: TaddooBadgeOptions): string;
|
|
647
|
+
/** Browser: insert the badge into an element or CSS selector; returns the link or null. */
|
|
648
|
+
export declare function mountTaddooBadge(target: Element | string, options?: TaddooBadgeOptions): HTMLAnchorElement | null;
|
|
600
649
|
export declare function readCheckoutReturn(opts?: { url?: string; clear?: boolean }): CheckoutReturn | null;
|
|
601
650
|
export declare function prepareImages(files: Iterable<File> | ArrayLike<File>, opts?: { maxSide?: number; quality?: number }): Promise<File[]>;
|
|
602
651
|
export default createTaddoo;
|
package/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* @taddoo/sdk v2.
|
|
2
|
+
* @taddoo/sdk v2.2.0 - official client for the Taddoo public website API 2.0.
|
|
3
3
|
* Connect a tattoo artist's or tattoo studio's own website to Taddoo, or
|
|
4
4
|
* rebuild the whole Taddoo page on an own domain: page design, flash
|
|
5
5
|
* designs, fixed services, consultations and touch-ups, request forms,
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
export const DEFAULT_BASE_URL = "https://taddoo.com/api/v1";
|
|
16
|
-
export const SDK_VERSION = "2.
|
|
16
|
+
export const SDK_VERSION = "2.2.0";
|
|
17
17
|
|
|
18
18
|
/** Error with the API's error code (e.g. "rejected", "card_required") and HTTP status. */
|
|
19
19
|
export class TaddooError extends Error {
|
|
@@ -429,4 +429,55 @@ export function createTaddoo(options = {}) {
|
|
|
429
429
|
return client;
|
|
430
430
|
}
|
|
431
431
|
|
|
432
|
+
/**
|
|
433
|
+
* Optional "Booking by Taddoo" badge (SDK 2.1). Entirely voluntary: nothing
|
|
434
|
+
* in the API depends on it. A small, visible link to taddoo.com that tells
|
|
435
|
+
* visitors where the booking runs. Returns an HTML string you can drop into
|
|
436
|
+
* a footer or next to the booking form.
|
|
437
|
+
* lang: "de" | "en" | "fr" | "es" | "it" | "nl" | "pt" (default "en")
|
|
438
|
+
* theme: "light" (dark text) | "dark" (light text) (default "light")
|
|
439
|
+
* text: your own wording instead of the default
|
|
440
|
+
*/
|
|
441
|
+
const BADGE_TEXT = {
|
|
442
|
+
de: "Buchung über",
|
|
443
|
+
en: "Booking by",
|
|
444
|
+
fr: "Réservation via",
|
|
445
|
+
es: "Reservas con",
|
|
446
|
+
it: "Prenotazioni con",
|
|
447
|
+
nl: "Boeken via",
|
|
448
|
+
pt: "Agendamento com",
|
|
449
|
+
};
|
|
450
|
+
|
|
451
|
+
function escapeHtml(v) {
|
|
452
|
+
return String(v).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
export function taddooBadgeHtml({ lang = "en", theme = "light", text } = {}) {
|
|
456
|
+
const lead = text !== undefined ? String(text) : BADGE_TEXT[String(lang).slice(0, 2).toLowerCase()] || BADGE_TEXT.en;
|
|
457
|
+
const color = theme === "dark" ? "#f4f4f5" : "#18181b";
|
|
458
|
+
const border = theme === "dark" ? "rgba(244,244,245,.25)" : "rgba(24,24,27,.15)";
|
|
459
|
+
return (
|
|
460
|
+
`<a href="https://taddoo.com" target="_blank" rel="noopener" class="taddoo-badge" ` +
|
|
461
|
+
`style="display:inline-flex;align-items:center;gap:6px;padding:6px 12px;border:1px solid ${border};border-radius:999px;` +
|
|
462
|
+
`font:500 12px/1.2 system-ui,-apple-system,sans-serif;color:${color};text-decoration:none">` +
|
|
463
|
+
`${escapeHtml(lead)} <strong style="font-weight:700">Taddoo</strong></a>`
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* Browser helper: insert the badge into an element (or a CSS selector).
|
|
469
|
+
* Returns the inserted <a> element, or null when the target is missing.
|
|
470
|
+
*/
|
|
471
|
+
export function mountTaddooBadge(target, options = {}) {
|
|
472
|
+
const doc = globalThis.document;
|
|
473
|
+
if (!doc) return null;
|
|
474
|
+
const el = typeof target === "string" ? doc.querySelector(target) : target;
|
|
475
|
+
if (!el) return null;
|
|
476
|
+
const wrap = doc.createElement("span");
|
|
477
|
+
wrap.innerHTML = taddooBadgeHtml(options);
|
|
478
|
+
const a = wrap.firstElementChild;
|
|
479
|
+
if (a) el.appendChild(a);
|
|
480
|
+
return a || null;
|
|
481
|
+
}
|
|
482
|
+
|
|
432
483
|
export default createTaddoo;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@taddoo/sdk",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "Official Taddoo SDK (website API 2.0): connect a tattoo artist's or tattoo studio's own website (built with Lovable, Bolt, v0, Cursor, Framer, Next.js, Astro...) to Taddoo, or rebuild the whole Taddoo page on an own domain. Page design, flash gallery with booking and deposit, consultations and touch-ups, fixed services (tattoo services, piercing, tooth gems) with booking, tattoo request form, availability, waitlists, gift vouchers and the merch shop, with the return to the own website after payment; requests and orders land in the artist's Taddoo app.",
|
|
5
5
|
"keywords": ["tattoo", "tattoo artist", "tattoo studio", "tattoo booking", "flash", "tattoo request form", "booking", "deposit", "consultation", "piercing", "link in bio", "merch shop", "gift vouchers", "taddoo", "website", "vibe coding", "lovable", "bolt", "v0", "cursor"],
|
|
6
6
|
"homepage": "https://taddoo.com/developers",
|