# 🌐 Zakeke Configurator for React 🚀

This library allows you to build a custom UI for the Zakeke 3D configurator tool. Create your own unique and immersive 3D experience with ease!

# 🔧 Prerequisites 🛠️

Before you can use this project, you will need to install Node.js, a JavaScript runtime built on Chrome's V8 JavaScript engine. Here's how to do it:

1. Download the latest version of Node.js from the [official Node.js website](https://nodejs.org/en/download/).

2. Run the installation file and follow the prompts in the Node.js Setup Wizard.

3. To confirm that Node.js was installed correctly, open your command prompt and run the following command:

```shell
node -v
```

You should see your installed version of Node.js displayed in the command prompt.


## 🛠️ Installation and Running a Basic Example 🏃‍♂️

To get started with this project, follow these steps:

1. Clone the project repository using the following command:

```shell
git clone https://github.com/UpCommerce/zakeke-configurator-react-example.git
```

2. Install the necessary dependencies:

```shell
npm install
```

3. Start the project:

```shell
npm run start
```

After running the example, a blank page will open on `localhost:3000`. This is because Zakeke requires some parameters to know what product to load. Let's get these parameters!

## 🎯 Getting the Parameters 🌐

Follow these steps to get all the parameters:

1. Go to the Zakeke configurator backoffice.

2. Add or edit a new product.

3. Navigate to the Shopping Preview page.

4. Right-click on the page -> inspect element.

5. Find the Zakeke `<iframe>` src attribute and copy all query string parameters.

6. Paste the parameters in the localhost page.

Your URL should look something like this:

```
http://localhost:3000?modelCode=XXX&culture=XX&token=XXXXXX....
```

With these parameters, you should see the product loading in a basic UI interface. Start customizing it to your needs!

## 📚 References 📖

The library exposes four key elements:

- `ZakekeEnvironment`: A class that contains the state of the current scene.

- `ZakekeProvider`: A React component that should wrap your application.

- `ZakekeViewer`: A React component that will render the 3D scene.

- `useZakeke`: An effect to retrieve data and execute methods.

Here is how a basic Zakeke theme would look like:

```jsx
const zakekeEnvironment = new ZakekeEnvironment();

<ZakekeProvider environment={zakekeEnvironment}>
	<div>
		<ZakekeViewer />
	</div>
</ZakekeProvider>
```

# 📚 Documentation Reference 📚

`useZakeke` exposes the following values:

```ts
    price: number;
	isOutOfStock: boolean;
	culture: string;
	currency: string;
	isSceneLoading: boolean;
	isAddToCartLoading: boolean;
	isInfoPointContentVisible: boolean;
	isViewerReady: boolean;
	fonts: FontFamily[];
	disableTextColors: boolean;
	defaultColor: string;
	textColors: RestrictionColor[];
	groups: Group[];
	templates: Template[];
	loadedComposition: { templateName: string; attributesOptions: Map<number, number>; selectedCategoryID: number | null } | null;
	currentTemplate: Template | null;
	items: Item[];
	productName: string;
	productCode: string;
	product: Product | null;
	cameras: Camera[];
	sellerSettings: SellerSettings | null;
	quantityRule: ProductQuantityRule | null;
	eventMessages: EventMessage[] | null;
	personalizedMessages: EventMessage[] | null;
	visibleEventMessages: VisibleEventMessage[];
	isAssetsLoading: boolean;
	draftCompositions: ThemeCompositions[] | null;
	additionalCustomProperties: { name: string; value: number; label: string; formatString: string }[] | null;
	currentAttributesSelection: object | null;
	currentCompositionInfo: { compositionId: string | null; compositionName: string | null; compositionTags: string[] | null } | null;
	translations: Translations | null;
	nftSettings: NftSettings | null;
	useLegacyScreenshot: boolean;
	isAIEnabled: boolean;
	uiConfig: UIConfig | null;
	backgroundColor: string;
	removeBackground: boolean;
	setMouseWheelZoomEnabled: (enabled: boolean) => void;
	selectOption: (optionId: number) => void;
	/**
	 * @internal
	 */
	internalAppendViewer: (container: HTMLElement) => void;
	getPrintingMethodsRestrictions: () => PrintingMethodsRestrictions;
	designUpdate: () => void;
	createQuote: (formData: any) => Promise<any>;
	setTemplate: (templateId: number) => Promise<void>;
	isAreaVisible: (areaId: number) => boolean;
	quantity: number;
	saveTemplate: (templateName: string, selectedCategoryID: number | null, attributesOptions: Map<number, number>) => Promise<void>;
	addToCart: (
		additionalProperties: Record<string, any>,
		OnBeforeSendDataToParent?: (data: OnBeforeSendDataToParent) => Promise<Record<string, any>>,
		legacyScreenshot?: boolean,
		nftForm?: NftForm
	) => Promise<void>;
	/**
	 * Create a PDF of the current configuration
	 * @returns The URL of the PDF
	 */
	getPDF: () => Promise<string>;
	getOnlineScreenshot: (
		width: number,
		height: number,
		legacyScreenshot?: boolean,
		backgroundColor?: string,
		padding?: number
	) => Promise<{ originalUrl: string; rewrittenUrl: string }>;

	setCamera: (id: string, onlyAngleOfView?: boolean, animate?: boolean) => void;
	setCameraByName: (name: string, onlyAngleOfView?: boolean, animate?: boolean) => void;
	setCameraZoomEnabled: (enabled: boolean) => void;
	resetCameraPivot: () => void;

	setCameraPivot: (meshId: string) => void;
	fullyLoadFont: (fontFamily: string | FontFamily) => Promise<FontFamily>;
	sanitizeString: (family: FontFamily, text: string) => string;
	getSanitationText: (family: FontFamily, text: string) => TextSanitationResult;
	getImages: (categoryId: number) => Promise<ZakekeImage[]>;
	getMacroCategories: () => Promise<ImageMacroCategory[]>;

	previewOnly__setItemImageFromBase64: (guid: string, base64: string) => void;
	setItemImageFromFile: (guid: string, file: File) => Promise<void>;
	addItemImage: (id: number, areaId: number) => Promise<void>;
	/**
	 * Upload an image and get the uploaded image
	 * @param file The file to upload
	 * @param progress A callback to get the upload progress
	 * @returns The uploaded image
	 */
	createImage: (file: File, progress?: (percentage: number) => void) => Promise<ZakekeImage>;
	createImageFromUrl: (url: string) => Promise<ZakekeImage>;
	setItemImage: (guid: string, imageId: number) => Promise<void>;
	setItemFontFamily: (guid: string, fontFamily: string) => void;
	setItemColor: (guid: string, color: string) => void;
	setItemBold: (guid: string, bold: boolean) => void;
	setItemItalic: (guid: string, italic: boolean) => void;
	setItemTextOnPath: (guid: string, areaId: number, value: boolean) => void;
	setItemText: (guid: string, text: string) => void;
	addItemText: (settings: { text: string; fontFamily: string }, areaId: number) => Promise<void>;
	/**
	 * Remove an item from the customization
	 * @param guid The guid of the item to remove
	 */
	removeItem: (guid: string) => Promise<void>;
	setQuantity: (quantity: number) => void;
	getShareCompositionUrl: () => Promise<string>;
	saveComposition: (customPreviewSize?: CustomPreviewSize) => Promise<void>;
	loadComposition: (id: string) => Promise<void>;
	switchFullscreen: () => void;
	openSecondScreen: () => void;
	isFullscreenMode: boolean;
	zoomIn: () => void;
	zoomOut: () => void;
	updateView: (adjustCamera?: boolean) => void;
	setHighlightSettings: (settings: { color: string; size: number }) => void;

	hasExplodedMode: () => boolean;
	isExplodedMode: boolean;
	setExplodedMode: (exploded: boolean) => void;

	// Scene manipulation
	getMeshIDbyName: (name: string) => string | undefined | null;
	hideMeshAndSaveState: (meshId: string) => void;
	restoreMeshVisibility: (meshId: string) => void;
	setMeshDesignVisibility: (meshId: string, visible: boolean) => void;

	// Events
	clearListeners: () => void;
	addFocusAttributesListener: (listenerFunction: FocusAttributesEventListener) => void;
	focusAttribute: (attributeId: number) => void;

	// Highlight
	highlightGroup: (groupId: number) => void;
	highlightAttribute: (attributeId: number) => void;
	// AR
	getQrCodeArUrl: (device: 'iOS' | 'Android') => Promise<string>;
	getMobileArUrl: (onFlyArUrl?: string) => Promise<Blob | string | null>;
	openArMobile: (url: string) => void;
	isSceneArEnabled: () => boolean;
	isArDeviceCompliant: () => boolean;
	IS_ANDROID: boolean;
	IS_IOS: boolean;

	setBackgroundColor: (color: string, alpha: number) => void;
	getTryOnUrl: (tryOnUrl?: string) => Promise<string>;
	isSceneTryOnEnabled: () => boolean;
	setCameraLocked: (isBlocked: boolean) => void;
	getTemplateUploadRestrictictions: (templateId: number, areaId: number) => TemplateUploadRestrictictions;
	saveDraftsComposition: (name: string, tags: string[], isCopy?: boolean) => Promise<void>;
	loadSavedComposition: (docID: string) => Promise<void>;
	deleteSavedComposition: (docId: string) => Promise<void>;
	exportSceneToGlb: () => Promise<string | Blob | null>;
	// reset, undo, redo
	reset: () => Promise<void>;
	undo: () => Promise<void>;
	redo: () => Promise<void>;

	// copyright checkbox
	getCopyrightMessageAccepted: () => boolean;
	setCopyrightMessageAccepted: (copyrightMandatoryCheckbox: boolean) => void;

	validationNFTEmail: (email: string) => boolean;
	validationNFTWalletAddress: (walletAddress: string) => boolean;

	templateMacroCategories: TemplateMacroCategory[] | null;
	applyTemplate: (templateGroupCompositionId: number) => Promise<void>;

	configureByAI: (text: string) => Promise<void>;
	hasVTryOnEnabled: boolean;
	canUseTryOn: boolean;
	canUsePD: boolean;
	getTryOnSettings: () => Zakeke.TryOnSettings | undefined;
	isTryOnMeshVisible: boolean;
	isMandatoryPD: boolean;
	isVisibleMeshShownForTryOn: boolean;
	setPDDistance: (distance: number) => void;
	pdDistance: () => number;
	exportTryOnMeshToGlb: () => Promise<string | Blob | null>;
```

You can find a full and updated list of the values exposed by `useZakeke` in the source code (the definitions files) of the library.
Please refer to the source code comments for a detailed understanding of each value and their usage.

_Note: Always ensure to handle these values appropriately in your React components to ensure a smooth user experience._