---
# System prepended metadata

title: Lancement de Kustom Web

---

# Lancement de Kustom Web

Depuis une page produit, `POST /api/v2/identified/{lang}/legacy/web_app/articles` puis redirection vers la page `{lang}/customizations/{id}/edit`

Peut être lancé aussi directement depuis une page produit de l'admin, qui redirigera vers `{lang}/create_customizations/{product_tag}` qui se charge de faire le create articles avant de rediriger vers `{lang}/customizations/{id}/edit?tag={product_tag}`

## Page Cheerz (CustomizationEditPage)
Elle récupère les infos venant du call customizations et différents éléments de Cheerz pour les transmettre à Kustom

- cheerz provider
- `GET /api/v2/identified/{lang}/legacy/web_app/providers`
    providers de photos (session, name, token)
- paramètres vidéo (videoMaxLength, videoMaxSize, videoMinLength)
- `GET /api/identified/3.1/customizations/{customizationId}`

    - config.additional_page_config
    - config.addons
    - config.display_format
    - config.display_title
    - config.display_page_name_plural
    - config.display_page_name_singular
    - config.is_zoom_out_enabled
    - config.max_column_count
    - config.min_column_count
    - config.max_page_count
    - config.min_page_count
    - config.page_addition_order
    - config.price
    - config.should_start_with_gallery
    - config.switch_products
    - article.gift_card.price
    - article.is_in_cart
    - article.memory_box
    - article.product
    - article.selected_addons

- `GET /api/v2/identified/{lang}/legacy/user_profiles`
    - fotomkey
    - isGuest

- `GET /api/identified/3.0/customizations/{customizationId}/content`
- `GET /api/identified/3.0/customizations/{customizationId}/template`

## Kustom (CustomizationContainer)
Attend un certain nombre de paramètres : 
- content
- finalize:
    - isActive (check si les paramètres sont remplis pour activer le bouton)
    - label
    - onFinalize (callBack, dislay upsells ou déclenche addArticle + redirection au panier)
- getOutsideCustomizationUploadPageUrl
- getSwitchProductPrice
- giftCardAmount
- isGuest
- isInCart
- onContentSave
- onDraftSave
- onOptionsChange
- onQuit (callBack, gère historique)
- productConfiguration
- providers
- summaryComponent
- template
- track (callBack, déclenche l'événement de tracking)
- tracking
- video

//TODO : séparer infos communes ^


Au lancement, ~~le CustomizationContainer récupère le content et le template grâce aux `kustomApiToken`, `templateRevisionId` et `contentId`~~

le content est aussitôt vérifié en fonction du template et pré rempli s'il manque des pages

Tout ce qui sera affiché dans la custo ensuite sera basé sur les content & template

```mermaid
classDiagram
    direction LR
    CustomizationContainerProps --* Configuration
    Configuration .. Price
    Configuration .. FinalizeConfiguration
    Configuration .. OptionsConfiguration
    Configuration .. PageName
    Configuration .. PagesConfiguration
    Configuration .. SubscriptionConfiguration
    Configuration .. ui
    Configuration .. VideoConfig
    
    
    class CustomizationContainerProps{
        +bool autorunGallery
        +String contentId
        +Configuration configuration
        +onQuit(CustomizationOnQuit)
        +String templateRevisionId
        +track(event: CustomizationEvent)?
    }
    
    class Configuration{
        +Price? additionalPrice
        +String? assetUrlTemplate
        +List~string~? characteristics
        +FinalizeConfiguration? finalize
        +Price giftCardAmount
        +bool? isGuest
        +bool? inCart
        +String? kustomApiToken
        +onSave()?
        +List~OptionsConfiguration~? options
        +String<"addition_asc"|"addition_desc">? orderedBy
        +PageName? pageName
        +PagesConfiguration? pages
        +Price? productPrice
        +SelectionProviderConnect? selectionProviderConnect
        +SelectionProviderDisconnect? selectionProviderDisconnect
        +List~SelectionProviderToken~? selectionProviderTokens
        +SubscriptionConfiguration? subscription
        +FunctionComponent<input>? summaryComponent
        +ui? ui
        +FotomProvider? fotomAPIProvider
        +FotomSelectionProvider? fotomSelectionProvider
        +BaseUploadProvider? uploadProvider
        +VideoConfig? video
        +bool? withZoomOut
    }
    
    class Price{
        +int cents
        +String currency
    }
    
    class FinalizeConfiguration{
        +String label
        +isActive(&lbrace;pagesCount: number&rbrace;)
        +onFinalize()
    }
    
    class OptionsConfiguration{
        +onChange()
        +String tag
        +String value
        +List~[values]~ values
    }
    
    OptionsConfiguration .. values
    
    class values{
        +String label
        +String? price
        +String value
    }
    
    class PageName{
        +String plural
        +String singular
    }
    
    class PagesConfiguration{
        +int? increment
        +int? max
        +int? min
    }
    
    class SubscriptionConfiguration{
        +int minPrintPerOrder
        +int remainingPrintToOrder
        +String validUntil
    }
    
    class ui{
        +int maxPagesByLine
        +int minPagesByLine
    }
    
    class VideoConfig{
        +String helpLink
        +int maxLength
        +int minLength
        +int maxWeight
    }
```
  

### Intéractions


```mermaid 
---
title: Passage des infos nécessaires
---
flowchart LR
    subgraph Kustom
        A
    end
    
    subgraph Cheerz
        B
    end
    
    B(CustomizationEditPage) -- CustomizationContainerProps --> A(CustomizationContainer)
```

```mermaid
---
title: onSave
---
flowchart TB
    subgraph Kustom
        A
    end
    
    subgraph Cheerz: CustomizationEditPage
        C
        D
        E
    end
    
    A(CustomizationContainer) -- props.onSave --> C{isGuest?}
    C -- yes --> D(Show LoginModal)
    C -- no --> E(Do Nothing)
```

```mermaid
---
title: onQuit
---
flowchart TB
    subgraph Kustom
        A
    end
    
    subgraph Cheerz: CustomizationEditPage
        C
        D
        E
    end
    
    A(CustomizationContainer) -- props.onQuit --> C{isChoice?}
    C -- yes --> D(Go back twice)
    C -- no --> E(Go back)
```

```mermaid
---
title: onFinalize
---
flowchart TB
    subgraph Kustom
        A
    end
    
    subgraph Cheerz: CustomizationEditPage
        C
        D
        E
        F
    end
    
    A(CustomizationContainer) -- props.configuration.finalize.onFinalize --> C{hasUpsells?}
    C -- yes --> D(UpsellsModal)
    D -- choose upsell --> E
    C -- no --> E(Add to cart)
    E --> F(Go to cart)
```


```mermaid
---
title: finalize - isActive
---
flowchart TB
    subgraph Cheerz: CustomizationEditPage
        C
    end
    
    subgraph Kustom
        A
    end
    
    A(CustomizationContainer) -- props.configuration.finalize.isActive(&lbrace;pagesCount&rbrace;) --> C{hasRequiredPages?}
    C -- yes --> A
    C -- no --> A
```

```mermaid
---
title: track
---
flowchart TB
    subgraph Kustom
        A
    end
    
    subgraph Cheerz: CustomizationEditPage
        B
    end
    
    subgraph Utils/tracking
        C
    end

    A(CustomizationContainer) -- props.track --> B(trackKustomWithContext) --> C(sendTrackEvent)

```


## Association des photos au content
```mermaid
classDiagram
    SelectionState .. SelectionPhoto
    
    class SelectionState{
        +List~SelectionPhoto~ photos
    }
    
    class SelectionPhoto {
        +String key
        +Length<"Unit.PX"> width
        +Length<"Unit.PX"> height
        +String? localRef
        +int? lastModified
        +SelectionProvider provider
        +String providerRef
        +String? providerThumbnailUrl
        +String? providerPictureUrl
        +String? fotomKey
        +String<"error"|"success"|"uploading">? status
    }
    
    Content .. ContentSelectionPhoto
    Content .. ContentPage
    ContentPage .. ContentEditablePicture
    
    class Content{
        <<Partial>>
        +List~ContentPage~ pages
        +List~ContentSelectionPhoto~ selectionPhotos
        +List~ContentSelectionPhoto~ galleryPhotos
        ...
    }
    
    class ContentSelectionPhoto {
        +String key
        +Length<"Unit.PX"> width
        +Length<"Unit.PX"> height
        +String? creationDate
        +ContentSelectionProvider provider
        +String providerRef
        +String? localRef      
        +String? fotomKey
    }
    
    class ContentPage{
        <<Partial>>
        +String pageDefinitionKey
        +ContentEditablePicture editablePictures
        +ContentEditableText editableTexts
        ...
    }
    
    class ContentEditablePicture{
        +String editablePictureKey
        +String selectionPhotoKey
        +Cropping cropping
        +FilterTag filterTag
    }
```

```mermaid
---
title: Choix et ajout d'une image
---
stateDiagram-v2
    User --> SelectionState: Select photo
    SelectionState --> EditablePicture: add Photo
    EditablePicture --> SelectionState: get URL\nfrom fotomKey
    EditablePicture --> Content
    Content --> SelectionState: get saved selection\nfrom content
```

```mermaid
---
title: Sauvegarde de la sélection et du content
---
stateDiagram-v2
    User --> SelectionState: Save or Add to cart
    SelectionState --> Selection: add used Selection
    SelectionState --> Gallery: add remaining Selection
    Selection --> Content
    Gallery --> Content
```

```mermaid
---
title: Display picture
---
sequenceDiagram
    EditablePicture ->> utils/customization/finders.ts: findEditablePictureDefinition(editablePictureKey,pageDefinition)
    utils/customization/finders.ts ->> EditablePicture: TemplateEditablePictureDefinition
    EditablePicture ->> SelectionState: findSelectionPhoto(selectionPhotoKey)
    SelectionState -->> EditablePicture: SelectionPhoto
    EditablePicture ->> utils/selection/finders.ts: findPhoto(selectionPhoto.fotomKey)
    utils/selection/finders.ts -->> EditablePicture: Url
```


```mermaid
---
title: While customizing
---
graph TB
SelectionPhoto -- key:\nref to SelectionPhoto --> selectionPhotoKey
selectionPhotoKey -- fotomKey:\nget fotomKey to construct image URL ---> SelectionPhoto
SelectionState --> editablePictures

subgraph "`**Content**`"
    direction LR
    pages
end

subgraph SelectionState
    SelectionPhoto
end

subgraph editablePictures
    direction TB
    selectionPhotoKey
end


subgraph pages
    direction TB
    editablePictures
end
```

```mermaid
graph TB
ContentSelectionPhoto -- key:\nref to ContentSelectionPhoto --> selectionPhotoKey
selectionPhotoKey -- fotomKey:\nget fotomKey to construct image URL ---> ContentSelectionPhoto
SelectionState --> editablePictures
SelectionState -- fill when saving (only with fotom key) --> ContentSelectionPhoto
SelectionState -- fill when saving --> galleryPhotos

subgraph "`**Content**`"
    direction LR
    pages
    selectionPhotos
    galleryPhotos
end

subgraph SelectionState
    SelectionPhoto
end

subgraph editablePictures
    direction TB
    selectionPhotoKey
end

subgraph selectionPhotos
    direction TB
    ContentSelectionPhoto
end

subgraph ContentSelectionPhoto
    direction TB

end

subgraph pages
    direction TB
    editablePictures
end

```

```mermaid
graph TB
ContentSelectionPhoto -- key:\nref to ContentSelectionPhoto --> selectionPhotoKey
selectionPhotoKey -- fotomKey:\nget fotomKey to construct image URL ---> ContentSelectionPhoto
SelectionState --> editablePictures
SelectionState -- fill when saving (only with fotom key) --> ContentSelectionPhoto
SelectionState -- fill when saving --> galleryPhotos

subgraph "`**Content**`"
    direction LR
    pages
    selectionPhotos
    galleryPhotos
end

subgraph SelectionState
    SelectionPhoto
end

subgraph editablePictures
    direction TB
    editablePictureKey
    selectionPhotoKey
    cropping
    filterTag
end

subgraph selectionPhotos
    direction TB
    ContentSelectionPhoto
end

subgraph ContentSelectionPhoto
    direction TB
    width
    height
    creationDate
    provider
    providerRef
    localRef
    fotomKey
    key
end

subgraph pages
    direction TB
    editablePictures
    ...
end

```

Au chargement on récupère les photos déjà présentes dans le content dans `galleryPhotos` et `selectionPhotos` pour remplir le `SelectionState` qui sert de référence pour remplir l'album tout au long de la custo.
On ajoute toutes les photos sélectionnées par l'utilisateur dans ce state tout au long de la custo et à la sauvegarde `SelectionState` est rebasculé dans le content entre `selectionPhotos` pour les photos utilisées (après vérification qu'elles ont bien toutes une clé fotom) et `galleryPhotos` pour celles qui ne le sont pas.
L'autofill via la sélection est disponible à partir du moment ou il n'y a plus d'upload en cours.

### Upload
Les photos sélectionnées en local sont envoyées dans la queue d'upload après qu'on ait récupéré les informations disponibles (lastModified, height, width)
Les photos de providers sont envoyées directement dans la queue d'upload, les infos nécessaires ont été récupérées en amont, depuis le provider

```mermaid
flowchart TD
    A(file) -- Get datas --> B("`**ContentSelectionPhoto**
      +key
      +lastModified
      +localRef
      +provider
      +providerPictureUrl
      +providerRef
      +providerThumbnailUrl
      +status
      +height
      +width
    `")
    B --> C[(SelectionState)]
    B --> D{Upload}
    D --x|Error:\nRemove from content| E
    D -->|OK:\nAdd fotom key| B
    C --> E[(ContentState)]

```

## Moteur de rendu

### Source de véritée
Les informations utiles à l'ensemble de la custo sont divisée en deux parties qui peuvent alimenter les composants à n'importe qu'elle niveau, dans des contextes.
Comme ceux-ci provoque le rafraicissement de tout élément y faisant appel, il ont été divisés en deux parties:
- une partie fixe, qui n'a pas à être mise à jour suite aux modifications de l'utilisateur: Le ConfigurationState. On y place toutes les infos de configurations passées à la custo et le template.
- une partie qui sera mise à jour tout au long de la custo: le CustomizationState. On y place le content mais également les informations de ui. (quel editeur/panel est ouvert, galerie, notifications, mapping des noms assignés aux pages, etc)

### Pages spéciales
Pour l'affichage de la customisation, on commence par différenciers les types de pages:
- les pages de type `extra` seront complètement ignorées
- les pages de couverture seront affichées séparément
    - types `front-cover`, `spine` --> `CoverContent`
    - types `front-envelope`, `back-envelope` --> `EnvelopeContent`
    - la page de type `back-cover` n'est affichée que dans la preview
- les pages de type `layflat-back-matter`, `layflat-front-matter`, `single-back-matter`, `single-front-matter` ne sont pas affichées
- le reste des pages sera géré dans le composant `PageList`

### Par typologie de produit
Suivant les types de produits ou la configuration on a besoin de mises en pages différentes. On a donc créé un utilitaire qui permet de parcourir les pages et de dispatcher le bon nombre de pages dans le bon composant de rendu.
- pages liées (`contentPageConfiguration.nextFilterTypeKeys.length > 1`)
- album double pages à spiral (`template.templateType === "book" && template.pageMode === "spiral_horizontal"`)
- album double pages (`template.templateType === "book" &&
    ["single_horizontal", "spiral_horizontal"].includes(template.pageMode)`)
- album layflat (`template.templateType === "book"` et pas les précédents)
- calendrier vertical (`template.templateType === "calendar" && template.pageMode === "single_vertical"`)
- page simple (tout le reste)


```mermaid
flowchart TB
CoverContent-- Add UI Layout and get cover pages-->PagePresentation
EnvelopeContent-- Add UI Layout and get cover pages-->PagePresentation
PageList-- getPagesLayoutFormat -->PageListItem
subgraph CustomizationContent
  direction LR
  CoverContent
  EnvelopeContent
  PageList
end
PagePresentation
PageListItem-- insert right number of pages into Chosen layout-->PagePresentation
subgraph PagePresentation
Page
end
subgraph PageListItem
direction TB
  PageLinkedLayout-. ou .-> BookDoubleHorizontalLayout
  BookDoubleHorizontalLayout-. ou .-> BookSpiralHorizontalLayout
  BookSpiralHorizontalLayout-. ou .-> CalendarVerticalLayout
  CalendarVerticalLayout-. ou .-> BookLayFlatLayout
  BookLayFlatLayout-. ou .-> SimpleLayout
  SimpleLayout
end
subgraph Page
  EditablePicture["`Box
**EditablePicture**
*EditablePictureSvg*`"]
  EditableText["`Box
  **EditableText**
*AdvancedTextArea*`"]
  ForcedImage["`Box
  **ForcedImage**`"]
  ForcedText["`Box
  **ForcedText**`"]
  Cell["`Box
  **Cell**
  *Cell elements*`"]
  DateText["`Box
  **DateText**`"]
  Video["`Box
  **Video**`"]
end
```
#### *PagePresentation
Elle sert à appliquer la présentation appliquée à la page (un cadre par exemple, c'est élément n'est pas imprimé).
Comme elle entoure toute la page, c'est également ici qu'on applique la shape de page, s'il y en a une.

#### *Box
Ce composant est ajouté autour de chaque élément de la page. Il permet de les positionner au sein de la page.


### Calcul de la taille des pages
Pour savoir dans quelle taille afficher chaque page et ses éléments, on crée un ratio: le PPMM (pixel par millimètre).
On calcul le PPMM en fonction de la taille d'affichage disponible et de la plus grande taille de page disponible (hors envelope).

Il y a deux cas possibles:
1.  **avec le paramètre pages_by_line**
    C'est le cas le plus simple.
    `pages_by_line_min_web` est pris en compte pour les petits écrans (jusqu'à 960px de large)
    `pages_by_line_max_web` est pris en compte pour les écrans plus grands
    On prend la taille disponible (moins les marges), on divise par le nombre de pages voulu, et o n'a plus qu'à calculer le ratio entre la taille de la plus grande page (en mm) et celle qu'on vient d'obtenir (en pixel)
2.  **sans pages_by_line**
    On aura une limite max horizontale et verticale par rapport à la taille de la zone d'affichage (le but est de pouvoir voir une page entière).
    On doit aussi déterminer si on doit calculer pour un page simple, une page double (vertical: calendriers, horizontal: album ou pages recto/verso), ou plus (pages liées)
    On se base aussi sur un jeu de tailles limite par types de produits
    On calcul le tout pour pouvoir mettre un maximum de pages (de la plus grande taille trouvée) dans l'espace disponible avec la taille minimum définie et on agrandi pour remplir l'espace. On prend la taille obtenue pour calculer le PPMM.

Si la taille de l'espace disponible est modifiée, on refait le calcul.

Ce PPMM est très important et on le retrouve à différents endroit car les composants servant à rendre un élément de page feront le calcul de leur propre taille à partir des dimensions fournies par le template et de ce PPMM

### Rendu Custo vs rendu des options
Le rendu de la custo dépend directement du template et du content.
Une page dans le content sera rendue par un composant Page (défini par une page_definition dans le template), et ses éléments également (élément de type editable_picture --> composant EditablePicture, editable_text --> EditableText, etc.)
Un composant Page servira donc à rendre aussi bien une page de couverture, une enveloppe, ou un print.
Tout ce qui vient du template sert à déterminer l'apparence du composant (taille, position), ce qui vient du content sert à le remplir (choix de la couleur, texte, photo).
Les options, elles, sont composées à partir de plusieurs conditions en fonction des besoin UX:
- les layouts de couverture et leurs couleurs sont donc représentés différemment des layouts de pages simples.
- on affiche une sélection de photos dans les panels d'options si le produit est un album, un calendrier, ou contient un layout de plus de 6 editablePictures.
- pour les calendriers on ajoute un onglet événements qui s'affichera au global pour toutes les pages alors que les autres onglets de panels (ou panels) sont liés à une page

## Appels d'API & échanges kustom/cheerz

```mermaid
sequenceDiagram
    participant front as Cheerz front
    participant frontk as Kustom front
    participant back as Cheerz back
    participant kustom as Kustom back
    
    front->>back: POST /api/v2/identified/{lang}/legacy/web_app/articles
    back-->>front: customizationId
    Note over front, back: redirection vers<br/>{lang}/customizations/{customizationId}/edit
    front->>back: GET /api/v2/identified/{lang}/legacy/web_app/providers
    back-->>front: fotomUserId, fotomUserToken, providers
    front->>back: GET /api/v2/identified/{lang}/legacy/web_app/customizations/{customizationId}
    back-->>front: kustom_user_token, kustom_content_ref, kustom_template_revision_ref, ...
    front->>back: GET /api/v2/identified/{lang}/legacy/user_profiles
    back-->>front: fotomKey, isGuest, ...
    front->>back: GET /api/v2/public/{lang}/{country_code3}/{currency_code3}/products/{productTag}/options?device=web
    back-->>front: product_options (upsells)
    
    rect rgb(235, 235, 235)
        note over front, kustom: Kustom
    
        frontk->>kustom: GET /template_revisions/{templateRevisionId}`<br/>body { definition_version_range: 13-13 }
        kustom-->>frontk: json template
        frontk->>kustom: GET /contents/{contentId}`<br/>body { definition_version_range: 13-13 }
        kustom-->>frontk: json content
        note right of frontk: Upload*
        note right of frontk: on Switch Product
        frontk->>front: onChange
        front->>back: PUT `/api/v2/identified/{lang}/legacy/web_app/articles/{articleId}`<br/>body {product_tag}
        front->>back: GET /api/v2/identified/{lang}/legacy/web_app/customizations/{customizationId}
        note right of frontk: on Save
        frontk->>front: onSave
        opt User is guest
        front->>frontk: login
        end
        frontk->>kustom: PUT /contents/{contentId}`<br/>body { content }
        kustom-->>frontk: json content
        note right of frontk: on Finalize
        frontk->>kustom: PUT /contents/{contentId}`<br/>body { content }
        kustom-->>frontk: json content
        frontk->>front: onFinalize
        opt has upsells
        front->>back: upsells choice then<br/>PUT `/api/v2/identified/{lang}/legacy/web_app/articles/{articleId}`<br/>body {options}
        end
        front->>back: PUT  /api/v2/identified/{lang}/legacy/web_app/article_add_to_cart/{articleId}
        Note over front, kustom: redirection vers {lang}/cart
    end
```
```mermaid   
sequenceDiagram
    box Upload
    participant frontk as Kustom front
    participant fotom as Fotom
    participant provider as Providers
    end
    
    frontk->>fotom: GET /user_galleries<br/>body {fotom_key} headers {Fotom-Token...}
    fotom-->>frontk: albums list with albumRefs
    frontk->>fotom: GET /user_galleries/{albumRefs}<br/>body {fotom_key, limit, page} headers {Fotom-Token...}
    fotom-->>frontk: albums contents (with selection photos data)
    opt from local files
        frontk->>fotom: POST /uploads body {file,provider_tag}
        fotom-->>frontk: {height_px, provider_tag, sha1, taken_at, updated_at, width_px}
    end
    opt from google photos
        frontk->>provider: POST https://photoslibrary.googleapis.com/{VERSION}/{edge} ...
        provider-->>frontk: albums or photos data
        frontk->>fotom: POST /remote_uploads body {file,provider_tag}
        fotom-->>frontk: {height_px, provider_tag, sha1, taken_at, updated_at, width_px}
    end
```

