@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 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.0.0 - official client for the Taddoo public website API 2.0.
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.0.0";
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, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
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.0.0",
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",